Challenge

Week 6 - Multivariate Newton Rhapson Solver

Calculating the solution to the non linear coupled ODEs by multivariate newton raphson method

Explanation of the method and approach

The derivative in the ODEs can be written by backward differencing i.e.

(y_1(n)-y_1(n-1))/dt=-0.04*y_1+(10)^4*y_2*y_3

(y_2(n)-y_2(n-1))/dt=0.04*y_1-(10)^4*y_2*y_3-3*(10)^7*(y_2)^2

(y_3(n)-y_3(n-1))/dt=3*(10)^7*(y_2)^2

If every thing is taken to the left hand side of the equation, then the ODEs can be written as:

Equation 1-----(y_1(n)-y_1(n-1))-dt*(-0.04*y_1+(10)^4*y_2*y_3)=0

Equation2------(y_2(n)-y_2(n-1))-dt*(0.04*y_1-(10)^4*y_2*y_3-3*(10)^7*(y_2)^2)=0

Equation3------(y_3(n)-y_3(n-1))-dt*(3*(10)^7*(y_2)^2)=0

Here, y(n-1) is the value at old time step, initially we assumed a value (given in the question, the value y at time step 0)of

y_0=1

y_1=0

y_2=0

This is the value at time step 0, running the iterations for 10 min i.e 600 sec

Taking dt =0.01

total 600/0.01=60000 no.of iterations will be run.

The multivariate newton raphson method is:

y(n)=y(n-1)-alpha*(inv(J)*F)

alpha=1

J=jacobian matrix=

df_1/dy_1 df_1/dy_2 df_1/dy_3

df_2/dy_1 df_2/dy_2 df_2/dy_3

df_3/dy_1 df_3/dy_2 df_3/dy_3

The jacobian matrix calculated numerically will give,

h=step increment taken for y_1, y_2 , y_3 =1e-4

doing forward differencing here,

df_1/dy_1=(f_1(y_1+h,y_2,y_3)-f_1(y_1,y_2,y_3))/h

df_1/dy_2=(f_1(y_1,y_2+h,y_3)-f_1(y_1,y_2,y_3))/h

df_1/dy_3=(f_1(y_1,y_2,y_3+h)-f_1(y_1,y_2,y_3))/h

df_2/dy_1=(f_2(y_1+h,y_2,y_3)-f_2(y_1,y_2,y_3))/h

df_2/dy_2=(f_2(y_1,y_2+h,y_3)-f_2(y_1,y_2,y_3))/h

df_2/dy_3=(f_2(y_1,y_2,y_3+h)-f_2(y_1,y_2,y_3))/h

df_3/dy_1=(f_3(y_1+h,y_2,y_3)-f_3(y_1,y_2,y_3))/h

df_3/dy_2=(f_3(y_1,y_2+h,y_3)-f_3(y_1,y_2,y_3))/h

df_3/dy_3=(f_3(y_1,y_2,y_3+h)-f_3(y_1,y_2,y_3))/h

We also assume guess values of y_1, y_2,y_3 to start performing the newton raphson technique iterations, here they are taken as

y1_guess=1

y2_guess=0

y3_guess=0

F matrix is a 3 x 1 matrix and

F[0]= value of function f_1 with guess values

F[1]= value of function f_2 with guess values

F[2]=value of function f_3 with guess values

The value at time step 1 is calculated by the assuming some initial guess values,

y(value at new time step)=y(value assumed)-alpha*inv(J(for assumed value))*F(at assumed value)

This iteration will be run till the error is greater than tolerance, and everytime the y_guess values are replaced with the new values of y.

This same process will be done for each time step, the final new value of y after each iteration that we get will be taken as value at that time step and for the next time step,it will act as the old time step value.

We guess a value everytime in the before starting the newton raphson iteration in every time step.

error=9e9

tolerance =1e-12

The code:

#final time loop
import numpy as np
from numpy.linalg import inv
'''
odes to solve are

4*x_1*x_1-625*x_2*x_2+2*x_3-1=0
20*x_3+np.exp(-x_1*x_2)+9=0


y_1=1
y_2=0
y_3=0
'''
Y_old=np.ones((3,1))
Y_old[0]=1
Y_old[1]=0
Y_old[2]=0
dt=0.01
h=1e-4
for t in np.arange(0,600,dt):
	
	def f_1(y_1,y_2,y_3):
		return y_1-Y_old[0]-dt*(-0.04*y_1+pow(10,4)*y_2*y_3)
	def f_2(y_1,y_2,y_3):
		return y_2-Y_old[1]-dt*(0.04*y_1-pow(10,4)*y_2*y_3-3*pow(10,7)*pow(y_2,2))
	def f_3(y_1,y_2,y_3):
		return y_3-Y_old[2]-dt*(3*pow(10,7)*pow(y_2,2))
	#defining jacobian
	def jacobian(y_1,y_2,y_3):
		J=  np.ones((3,3))
		#derivative of f_1
		J[0,0]=(f_1(y_1+h,y_2,y_3)-f_1(y_1,y_2,y_3))/h
		J[0,1]=(f_1(y_1,y_2+h,y_3)-f_1(y_1,y_2,y_3))/h
		J[0,2]=(f_1(y_1,y_2,y_3+h)-f_1(y_1,y_2,y_3))/h
		#derivative of f_2
		J[1,0]=(f_2(y_1+h,y_2,y_3)-f_2(y_1,y_2,y_3))/h
		J[1,1]=(f_2(y_1,y_2+h,y_3)-f_2(y_1,y_2,y_3))/h
		J[1,2]=(f_2(y_1,y_2,y_3+h)-f_2(y_1,y_2,y_3))/h
		#derivative of f_3
		J[2,0]=(f_3(y_1+h,y_2,y_3)-f_3(y_1,y_2,y_3))/h
		J[2,1]=(f_3(y_1,y_2+h,y_3)-f_3(y_1,y_2,y_3))/h
		J[2,2]=(f_3(y_1,y_2,y_3+h)-f_3(y_1,y_2,y_3))/h
		return J


	# we copied the 3 x 1 matrix of x_old to just save some lines of creating new np.ones((3,1)) column matrix for F matrix, F is totally different
	#from x_old matrix, we could have also defined a different matrix as F=np.ones((3,1)) for F, because anyhow we are changing its value and re
	#defining the value of elements of F inside the loop, we just need a skeleton 3 x 1 matrix having 1 as each element to have a matrix'body'
	
	error=9e9
	
	F=np.ones((3,1))
	tol=1e-12
	alpha=1
	iter=0
	Y_guess=np.ones((3,1))
	Y_guess[0]=1
	Y_guess[1]=0
	Y_guess[2]=0
	while error > tol:
		J=jacobian(Y_guess[0],Y_guess[1],Y_guess[2]);
		F[0]=f_1(Y_guess[0],Y_guess[1],Y_guess[2]);
		F[1]=f_2(Y_guess[0],Y_guess[1],Y_guess[2]);
		F[2]=f_3(Y_guess[0],Y_guess[1],Y_guess[2]);
		Y_new=Y_guess-alpha*np.matmul(inv(J), F)
		error=np.max(np.abs(Y_new-Y_guess))
		
		Y_guess=Y_new
		iter=iter+1
		#log_message='iteration # = {0} y_1 ={1} y_2 = {2} y_3 ={3}'.format(iter,Y_new[0],Y_new[1],Y_new[2])
		#print(log_message)
	#print(error)	
	#print(iter)
	Y_old=Y_new
	print(t)
	log_message='iteration # = {0} y_1 ={1} y_2 = {2} y_3 ={3}'.format(iter,Y_new[0],Y_new[1],Y_new[2])
	print(log_message)
	#print(f_1(Y_new[0],Y_new[1],Y_new[2]))
	y1_final=Y_new[0]
	y2_final=Y_new[1]
	y3_final=Y_new[2]
	
	#print(y1_final)
	#print(y2_final)
	#print(y3_final)
	#print(error)
print('finish')

Observation:

The last five iterations are given here,

599.95
iteration # = 19 y_1 =[0.39993503] y_2 = [2.63134078e-06] y_3 =[0.60006234]
5.260236690673992e-13
599.96
iteration # = 19 y_1 =[0.39993296] y_2 = [2.63131842e-06] y_3 =[0.60006441]
5.259126467649367e-13
599.97
iteration # = 19 y_1 =[0.39993088] y_2 = [2.63129605e-06] y_3 =[0.60006649]
5.259126467649367e-13
599.98
iteration # = 19 y_1 =[0.3999288] y_2 = [2.63127369e-06] y_3 =[0.60006857]
5.259126467649367e-13
599.99
iteration # = 19 y_1 =[0.39992672] y_2 = [2.63125132e-06] y_3 =[0.60007064]

The last iteration will be, after end of 10 minutes.

y_1=0.39992672

y_2=2.63125132e-06

y_3=0.60007064

As this is a implicit solution, the dt value does not have a restriction because the answer is always stable but keeping the accuracy in mind , we should take low values of time step . Here we take time step as 0.01 , but taking the time step as 0.1 also gives us the same answer, i.e taking a bigger time step also gives the accurate answer here, so the taken time step works as ideal time step here.

The answer comes at time step 0.1 comes as,

y_1=0.39997575

y_2=2.63177929e-06

y_3=0.6000216

which is the same as that when time step is 0.01, so the accuracy is maintained even at 0.1 time step.

Your portfolio, next

Ship work like this
in your own course.

Talk to a career counsellor

Free 20-minute call. We'll map the right course to where you want to work.

By submitting you agree to our Terms and Privacy Policy.

Talk to a career counsellor

Free 20-minute call. We'll map the right course to where you want to work.

By submitting you agree to our Terms and Privacy Policy.