Challenge

Week 6 - Multivariate Newton Rhapson Solver

Aim

To solve ODE's using multivariant Newton Raphson method

Introduction

The Newton Raphson method is used to get the solutions of nonlinear coupled equations system. The Newton-Raphson method (also known as Newton's method) is a way to find an approximation for the root of a real-valued function f(x)=0">f(x)=0. The method rely on the idea that a continuous and differentiable function can be approximated by a straight-line tangent to it.

Following ODE's are to be solved using Newton Raphson method

dy1/dt = -0.04y1 + 104y2y3

dy2/dt = 0.04y1 - 104y2y3-3 * 107y22

dy3/dt = 3* 107y22

we have initial guess values:

y1=1

y2=0

y3=0

For all of the below equation superscript 'n' shows new node or new timestep while 'n-1' shows previous or old timestep or node.

By using BDM,

yn-yn-1 / Δt = C

yn - (Δt×C) = yn-1

So above ODE's can be calculated as,

y1n-y1n-1 / Δt = (-0.04y1 + 104y2y3)n

y2n-y2n-1 / Δt = (0.04y1 - 104y2y3-3 * 107y2^2)n

y3n-y3n-1 / Δt = (3* 107y22)n

Hence we get system of linear equations,

F1n => y1n-1 +(Δt) (-0.04y1 + 104y2y3)n -y1n=0

F2n => y2n-1 +(Δt) (0.04y1 - 104y2y3-3 * 107y22)n -y2n=0

F3n => y3n-1 +(Δt) (3* 107y22)n -y3n=0

NOTE:

For solving the actual problem using Python, we proceed as usual and solve the quadratic equations first by defining functions for each equation. An arbitraty dy value is assumed for the accurate discretization of the PDE. In this case, dy = 1e-10.

Guess values are assumed as an array which will be used in the time loop required for the problem. We simulate the system for 10 mins, i.e, for 600 seconds.

import numpy as np 
import matplotlib.pyplot as plt
from numpy.linalg import inv

# Functions to solve each multivariable equation(as shown above)
def f1(y1,y2,y3,y_old1,h): 
	return y_old1 + h*(-0.04*y1 + (pow(10,4)*y2*y3)) - y1

def f2(y1,y2,y3,y_old2,h): 
	return y_old2 + h*(0.04*y1 - (pow(10,4)*y2*y3) - (3*pow(10,7)*pow(y2,2))) - y2

def f3(y1,y2,y3,y_old3,h): 
	return y_old3 + h*(3*pow(10,7)*pow(y2,2)) - y3

# function for the jacobian matrix 
def jacobian(y_new,y_old,h):

	# input an identity 3x3 matrix
	J = np.ones((3,3))

	# Using guess values that will be updated for each time step when solving
	y1 = y_new[0] 
	y2 = y_new[1] 
	y3 = y_new[2] 

	# Old values are the input guess values we give
	y_old1 = y_old[0]
	y_old2 = y_old[1]
	y_old3 = y_old[2]

	# To solve the system numerically
	dy = 1e-10

	J[0,0] = (f1(y1+dy,y2,y3,y_old1,h) - f1(y1,y2,y3,y_old1,h))/dy
	J[0,1] = (f1(y1,y2+dy,y3,y_old1,h) - f1(y1,y2,y3,y_old1,h))/dy
	J[0,2] = (f1(y1,y2,y3+dy,y_old1,h) - f1(y1,y2,y3,y_old1,h))/dy

	J[1,0] = (f2(y1+dy,y2,y3,y_old1,h) - f2(y1,y2,y3,y_old1,h))/dy
	J[1,1] = (f2(y1,y2+dy,y3,y_old1,h) - f2(y1,y2,y3,y_old1,h))/dy
	J[1,2] = (f2(y1,y2,y3+dy,y_old1,h) - f2(y1,y2,y3,y_old1,h))/dy

	J[2,0] = (f3(y1+dy,y2,y3,y_old1,h) - f3(y1,y2,y3,y_old1,h))/dy
	J[2,1] = (f3(y1,y2+dy,y3,y_old1,h) - f3(y1,y2,y3,y_old1,h))/dy
	J[2,2] = (f3(y1,y2,y3+dy,y_old1,h) - f3(y1,y2,y3,y_old1,h))/dy

	return J

# Input the guess values as old, due to use of backward differencing
y_old = np.zeros((3,1))
y_old[0] = 1

y_new = np.zeros((3,1))

# numpy column vector for F matrix
F = np.copy(y_new)

# inputs
a = 1 # alpha for Newton raphson solver(given in problem statement)
tol = 1e-10
error = 1e9

dt = 0.1 # time step
h = dt

# simulation time = 10 mins = 600 secs
t = np.arange(0,600,h)# divide 600 secs in h time steps

# Empty arrays initialized for storing final values
e = np.zeros((int(600/h),1))
Y1 = np.zeros((int(600/h),1))
Y2 = np.zeros((int(600/h),1))
Y3 = np.zeros((int(600/h),1))

# time loop started
for i in range(0,len(t)):
	y_old1 = y_old[0]
	y_old2 = y_old[1]
	y_old3 = y_old[2]
	y_guess = np.copy(y_old)

	while error > tol:
		# computing jacobian - old values used
		J = jacobian(y_new,y_old,h)

		y1g = y_guess[0]
		y2g = y_guess[1]
		y3g = y_guess[2]

		# Computing F vector - old values used
		F[0] = f1(y1g,y2g,y3g,y_old1,h)
		F[1] = f2(y1g,y2g,y3g,y_old2,h)
		F[2] = f3(y1g,y2g,y3g,y_old3,h)

		# computing new values
		y_new = y_guess - a*np.matmul(inv(J),F)
		error = max(abs(y_new-y_guess))

		y_guess = y_new
	
	# Values after each time step added into array
	e[i] = error
	y_old = y_new
	Y1[i] = y_new[0]
	Y2[i] = y_new[1]
	Y3[i] = y_new[2]

	error = 1e9

# Plots
plt.plot(t,Y1, label = 'y1')
plt.plot(t,Y2, label = 'y2')
plt.plot(t,Y3, label = 'y3')
plt.title('Multi-variate Newton Raphson Solver for dt = ' +str(h))
plt.xlabel('Time (secs)')
plt.ylabel('$y_i$')
plt.legend()
plt.grid()
plt.show()

PLOTS

Observations:

i) Change in time step doesn't make any big difference in final solution.

ii) Small value of time step takes longer time (iterations) to converged result which may increase cost.

iii) Hence, an average dt value of 0.1 is assumed in this case to give an accurate representation of data and also sufficiently time efficient.

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.