Sunday, January 5, 2014

Solving ODEs

This was an error I am surprised I was even able to locate to the solver because I thought it was something related to the circuit solver. So far, the ODE is solved as an entire state vector at a time. This means, all states are calculated together and when one state needs the information of another state it used the previous value of the state. So even if a more updated value of a state is available, with this method the previous value is used.

This actually defeats the purpose of solving ODEs in a backward manner. That is we solve from the last state to the first state since the matrices have been made upper triangular.

So, the ODE was changed such that a particular state is calculated completely, its value is updated and this updated value is available for the next state to be calculated.

Here is the code (click on "view raw" below the code box to view the code in another window):

def mat_ode(matrix_e, matrix_a, matrix_b, state_vector, input_vector, dt, ode_vectors, sys_loops, list_of_nodes):
""" Solves the ODE using Runge Kutta 4th order method. """
def runge_function4(x_plus_dxdt, ddt_mat, dbydt_order, ode_row):
""" Defines the function dx/dt=f(x) for 4th order
Runge Kutta solver. """
#Calculate d/dt vector in reverse.
#for c1 in range(matrix_e.rows-1, -1, -1):
if matrix_e.data[ode_row][ode_row]:
try:
if input_vector.rows:
pass
except:
if not (matrix_b.columns==1):
print "Input signal has to be a real number and not a vector."
else:
ddt_mat[dbydt_order-1].data[ode_row][0]=matrix_b.data[ode_row][0]*input_vector
else:
if not (matrix_b.columns==input_vector.rows):
print "Dimension of input vector incorrect."
else:
ddt_mat[dbydt_order-1].data[ode_row][0]=0.0 # Added on Dec. 29, 2012.
for c2 in range(matrix_b.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]+=matrix_b.data[ode_row][c2]*input_vector.data[c2][0]
if (dbydt_order==2):
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*0.5*ddt_mat[dbydt_order-2].data[c3][0]
if (dbydt_order==3):
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*0.5*ddt_mat[dbydt_order-2].data[c3][0]
if (dbydt_order==4):
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-2].data[c3][0]
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*x_plus_dxdt.data[c3][0]
ddt_mat[dbydt_order-1].data[ode_row][0]=ddt_mat[dbydt_order-1].data[ode_row][0]*dt
for c3 in range(ode_row+1, matrix_e.columns):
# for c3 in range(matrix_e.columns):
if not ode_row==c3:
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_e.data[ode_row][c3]*ode_vectors[4].data[c3][0]
ddt_mat[dbydt_order-1].data[ode_row][0]=ddt_mat[dbydt_order-1].data[ode_row][0]
# ddt_mat[dbydt_order-1].data[ode_row][0]=ddt_mat[dbydt_order-1].data[ode_row][0]/matrix_e.data[ode_row][ode_row]
else:
if not (matrix_a.data[ode_row][ode_row]):
#The variable has no dynamics and can't even be calculated statically!
# May be due to a redundant loop.
ddt_mat[dbydt_order-1].data[ode_row][0]=0.0
state_vector[0].data[ode_row][0]=0.0
state_vector[1].data[ode_row][0]=0.0
else:
ddt_mat[dbydt_order-1].data[ode_row][0]=0.0
state_vector[0].data[ode_row][0]=0.0
state_vector[1].data[ode_row][0]=0.0
try:
if input_vector.rows:
pass
except:
if not (matrix_b.columns==1):
print "Input signal has to be a real number and not a vector."
else:
state_vector[0].data[ode_row][0]+=matrix_b.data[ode_row][0]*input_vector
state_vector[1].data[ode_row][0]=state_vector[0].data[ode_row][0]
else:
if not (matrix_b.columns==input_vector.rows):
print "Dimension of input vector incorrect."
else:
for c2 in range(matrix_b.columns):
state_vector[0].data[ode_row][0]+=matrix_b.data[ode_row][c2]*input_vector.data[c2][0]
state_vector[1].data[ode_row][0]=state_vector[0].data[ode_row][0]
for c2 in range(matrix_a.columns):
if not (ode_row==c2):
state_vector[0].data[ode_row][0]-=matrix_a.data[ode_row][c2]*state_vector[1].data[c2][0]
state_vector[1].data[ode_row][0]=state_vector[0].data[ode_row][0]
state_vector[0].data[ode_row][0]=state_vector[0].data[ode_row][0]/matrix_a.data[ode_row][ode_row]
state_vector[1].data[ode_row][0]=state_vector[0].data[ode_row][0]
return
def runge_function5(x_plus_dxdt, ddt_mat, dbydt_order, ode_row):
""" Defines the function dx/dt=f(x) for higher order
ODE solver """
#Calculate d/dt vector in reverse.
#for c1 in range(matrix_e.rows-1, -1, -1):
if matrix_e.data[ode_row][ode_row]:
try:
if input_vector.rows:
pass
except:
if not (matrix_b.columns==1):
print "Input signal has to be a real number and not a vector."
else:
ddt_mat[dbydt_order-1].data[ode_row][0]=matrix_b.data[ode_row][0]*input_vector
else:
if not (matrix_b.columns==input_vector.rows):
print "Dimension of input vector incorrect."
else:
ddt_mat[dbydt_order-1].data[ode_row][0]=0.0 # Added on Dec. 29, 2012.
for c2 in range(matrix_b.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]+=matrix_b.data[ode_row][c2]*input_vector.data[c2][0]
if (dbydt_order==2):
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-2].data[c3][0]
if (dbydt_order==3):
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*0.5*ddt_mat[dbydt_order-3].data[c3][0]
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*0.5*ddt_mat[dbydt_order-2].data[c3][0]
if (dbydt_order==4):
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-4].data[c3][0]*(14.0/64.0)
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-3].data[c3][0]*(5.0/64.0)
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-2].data[c3][0]*(-3.0/64.0)
if (dbydt_order==5):
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-5].data[c3][0]*(-12.0/96.0)
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-4].data[c3][0]*(-12.0/96.0)
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-3].data[c3][0]*(8.0/96.0)
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-2].data[c3][0]*(64.0/96.0)
if (dbydt_order==6):
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-5].data[c3][0]*(-9.0/64.0)
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-4].data[c3][0]*(5.0/64.0)
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-3].data[c3][0]*(16.0/64.0)
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*ddt_mat[dbydt_order-2].data[c3][0]*(36.0/64.0)
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_a.data[ode_row][c3]*x_plus_dxdt.data[c3][0]
ddt_mat[dbydt_order-1].data[ode_row][0]=ddt_mat[dbydt_order-1].data[ode_row][0]*dt
for c3 in range(matrix_e.columns):
if not ode_row==c3:
ddt_mat[dbydt_order-1].data[ode_row][0]-=matrix_e.data[ode_row][c3]*ode_vectors[4].data[c3][0]
ddt_mat[dbydt_order-1].data[ode_row][0]=ddt_mat[dbydt_order-1].data[ode_row][0]
else:
if not (matrix_a.data[ode_row][ode_row]):
#The variable has no dynamics and can't even be calculated statically!
# May be due to a redundant loop.
state_vector[0].data[ode_row][0]=0.0
state_vector[1].data[ode_row][0]=0.0
else:
ddt_mat[dbydt_order-1].data[ode_row][0]=0.0
state_vector[0].data[ode_row][0]=0.0
state_vector[1].data[ode_row][0]=0.0
try:
if input_vector.rows:
pass
except:
if not (matrix_b.columns==1):
print "Input signal has to be a real number and not a vector."
else:
state_vector[0].data[ode_row][0]+=matrix_b.data[ode_row][0]*input_vector
state_vector[1].data[ode_row][0]=state_vector[0].data[ode_row][0]
else:
if not (matrix_b.columns==input_vector.rows):
print "Dimension of input vector incorrect."
else:
for c2 in range(matrix_b.columns):
state_vector[0].data[ode_row][0]+=matrix_b.data[ode_row][c2]*input_vector.data[c2][0]
state_vector[1].data[ode_row][0]=state_vector[0].data[ode_row][0]
for c2 in range(matrix_a.columns):
if not (ode_row==c2):
state_vector[0].data[ode_row][0]-=matrix_a.data[ode_row][c2]*state_vector[1].data[c2][0]
state_vector[1].data[ode_row][0]=state_vector[0].data[ode_row][0]
state_vector[0].data[ode_row][0]=state_vector[0].data[ode_row][0]/matrix_a.data[ode_row][ode_row]
state_vector[1].data[ode_row][0]=state_vector[0].data[ode_row][0]
return
def trapezoidal_mthd(x_plus_dxdt, ddt_mat, dbydt_order):
""" Defines the function dx/dt=f(x) for Trapezoidal
method. """
#Calculate d/dt vector in reverse.
for c1 in range(matrix_e.rows-1, -1, -1):
if matrix_e.data[c1][c1]:
try:
if input_vector.rows:
pass
except:
if not (matrix_b.columns==1):
print "Input signal has to be a real number and not a vector."
else:
ddt_mat[dbydt_order-1].data[c1][0]=matrix_b.data[c1][0]*input_vector
else:
if not (matrix_b.columns==input_vector.rows):
print "Dimension of input vector incorrect."
else:
ddt_mat[dbydt_order-1].data[c1][0]=0.0 # Added on Dec. 29, 2012.
for c2 in range(matrix_b.columns):
ddt_mat[dbydt_order-1].data[c1][0]+=matrix_b.data[c1][c2]*input_vector.data[c2][0]
for c3 in range(matrix_a.columns):
ddt_mat[dbydt_order-1].data[c1][0]-=matrix_a.data[c1][c3]*x_plus_dxdt.data[c3][0]
ddt_mat[dbydt_order-1].data[c1][0]=ddt_mat[dbydt_order-1].data[c1][0]*dt
for c3 in range(matrix_e.columns):
if not c1==c3:
ddt_mat[dbydt_order-1].data[c1][0]-=matrix_e.data[c1][c3]*ode_vectors[2].data[c3][0]
ddt_mat[dbydt_order-1].data[c1][0]=ddt_mat[dbydt_order-1].data[c1][0]
else:
if not (matrix_a.data[c1][c1]):
#The variable has no dynamics and can't even be calculated statically!
# May be due to a redundant loop.
ddt_mat[dbydt_order-1].data[c1][0]=0.0
state_vector[0].data[c1][0]=0.0
state_vector[1].data[c1][0]=0.0
else:
ddt_mat[dbydt_order-1].data[c1][0]=0.0
state_vector[0].data[c1][0]=0.0
state_vector[1].data[c1][0]=0.0
try:
if input_vector.rows:
pass
except:
if not (matrix_b.columns==1):
print "Input signal has to be a real number and not a vector."
else:
state_vector[0].data[c1][0]+=matrix_b.data[c1][0]*input_vector
state_vector[1].data[c1][0]=state_vector[0].data[c1][0]
else:
if not (matrix_b.columns==input_vector.rows):
print "Dimension of input vector incorrect."
else:
for c2 in range(matrix_b.columns):
state_vector[0].data[c1][0]+=matrix_b.data[c1][c2]*input_vector.data[c2][0]
state_vector[1].data[c1][0]=state_vector[0].data[c1][0]
for c2 in range(matrix_a.columns):
if not (c1==c2):
state_vector[0].data[c1][0]-=matrix_a.data[c1][c2]*state_vector[0].data[c2][0]
state_vector[1].data[c1][0]=state_vector[0].data[c1][0]
state_vector[0].data[c1][0]=state_vector[0].data[c1][0]/matrix_a.data[c1][c1]
state_vector[1].data[c1][0]=state_vector[0].data[c1][0]
return
# Trapezoidal method
## trapezoidal_mthd(state_vector[0], ode_vectors, 2) #k1=dt*(dx/dt)
## ode_vectors[2].data[c1][0]=(ode_vectors[0].data[c1][0]+ode_vectors[1].data[c1][0])/2.0
## state_vector[1].data[c1][0]=state_vector[0].data[c1][0]+ode_vectors[2].data[c1][0]
## ode_vectors[0].data[c1][0]=ode_vectors[1].data[c1][0]
# Runge-Kutta 4th order method
for c1 in range(matrix_e.rows-1, -1, -1):
runge_function4(state_vector[0], ode_vectors, 1, c1) #k1=dt*(dx/dt)
runge_function4(state_vector[0], ode_vectors, 2, c1) #k2=dt*d/dt(x+k1/2)
runge_function4(state_vector[0], ode_vectors, 3, c1) #k3=dt*d/dt(x+k2/2)
runge_function4(state_vector[0], ode_vectors, 4, c1) #k4=dt*d/dt(x+k3)
ode_vectors[4].data[c1][0]=(ode_vectors[0].data[c1][0]+2.0*ode_vectors[1].data[c1][0]+2.0*ode_vectors[2].data[c1][0]+ode_vectors[3].data[c1][0])/6.0
state_vector[1].data[c1][0]=state_vector[0].data[c1][0]+ode_vectors[4].data[c1][0]
# Runge-Kutta 5th order method
# for c1 in range(state_vector[1].rows):
# runge_function5(state_vector[0], ode_vectors, 1, c1) #k1=dt*(dx/dt)
# runge_function5(state_vector[0], ode_vectors, 2, c1) #k2=dt*d/dt(x+k1)
# runge_function5(state_vector[0], ode_vectors, 3, c1) #k3=dt*d/dt(x+(k1+k2)/2)
# runge_function5(state_vector[0], ode_vectors, 4, c1) #k4=dt*d/dt(x+(14k1+5k2-3k3)/64)
# runge_function5(state_vector[0], ode_vectors, 5, c1) #k4=dt*d/dt(x+(-12k1-12k2+8k3+64k4)/96)
# runge_function5(state_vector[0], ode_vectors, 6, c1) #k4=dt*d/dt(x+(-9k2+5k3+16k4+36k5)/64)
# ode_vectors[4].data[c1][0]=(ode_vectors[0].data[c1][0]+2.0*ode_vectors[1].data[c1][0]+2.0*ode_vectors[2].data[c1][0]+ode_vectors[3].data[c1][0])/6.0
# state_vector[1].data[c1][0]=state_vector[0].data[c1][0]+ode_vectors[4].data[c1][0]
## ode_vectors[6].data[c1][0]=7.0*ode_vectors[0].data[c1][0]
## ode_vectors[6].data[c1][0]+=12.0*ode_vectors[2].data[c1][0]
## ode_vectors[6].data[c1][0]+=7.0*ode_vectors[3].data[c1][0]
## ode_vectors[6].data[c1][0]+=32.0*ode_vectors[4].data[c1][0]
## ode_vectors[6].data[c1][0]+=32.0*ode_vectors[5].data[c1][0]
## ode_vectors[6].data[c1][0]=ode_vectors[6].data[c1][0]/90.0
## state_vector[1].data[c1][0]=state_vector[0].data[c1][0]+ode_vectors[6].data[c1][0]
return
view raw ode.py hosted with ❤ by GitHub

No comments:

Post a Comment