Solving system of coupled differential equations using Runge-Kutta in python - python

This python code can solve one non- coupled differential equation:
import numpy as np
import matplotlib.pyplot as plt
import numba
import time
start_time = time.clock()
#numba.jit()
# A sample differential equation "dy / dx = (x - y**2)/2"
def dydx(x, y):
return ((x - y**2)/2)
# Finds value of y for a given x using step size h
# and initial value y0 at x0.
def rungeKutta(x0, y0, x, h):
# Count number of iterations using step size or
# step height h
n = (int)((x - x0)/h)
# Iterate for number of iterations
y = y0
for i in range(1, n + 1):
"Apply Runge Kutta Formulas to find next value of y"
k1 = h * dydx(x0, y)
k2 = h * dydx(x0 + 0.5 * h, y + 0.5 * k1)
k3 = h * dydx(x0 + 0.5 * h, y + 0.5 * k2)
k4 = h * dydx(x0 + h, y + k3)
# Update next value of y
y = y + (1.0 / 6.0)*(k1 + 2 * k2 + 2 * k3 + k4)
# Update next value of x
x0 = x0 + h
return y
def dplot(start,end,steps):
Y=list()
for x in np.linspace(start,end,steps):
Y.append(rungeKutta(x0, y, x , h))
plt.plot(np.linspace(start,end,steps),Y)
print("Execution time:",time.clock() - start_time, "seconds")
plt.show()
start,end = 0, 10
steps = end* 100
x0 = 0
y = 1
h = 0.002
dplot(start,end,steps)
This code can solve this differential equation:
dydx= (x - y**2)/2
Now I have a system of coupled differential equations:
dydt= (x - y**2)/2
dxdt= x*3 + 3y
How can I implement these two as a system of coupled differential equations in the above code?
Is there any more generalized way for system of n-number of coupled differential equations?

With the help of others, I got to this:
import numpy as np
from math import sqrt
import matplotlib.pyplot as plt
import numba
import time
start_time = time.clock()
a=1
b=1
c=1
d=1
# Equations:
#numba.jit()
#du/dt=V(u,t)
def V(u,t):
x, y, vx, vy = u
return np.array([vy,vx,a*x+b*y,c*x+d*y])
def rk4(f, u0, t0, tf , n):
t = np.linspace(t0, tf, n+1)
u = np.array((n+1)*[u0])
h = t[1]-t[0]
for i in range(n):
k1 = h * f(u[i], t[i])
k2 = h * f(u[i] + 0.5 * k1, t[i] + 0.5*h)
k3 = h * f(u[i] + 0.5 * k2, t[i] + 0.5*h)
k4 = h * f(u[i] + k3, t[i] + h)
u[i+1] = u[i] + (k1 + 2*(k2 + k3 ) + k4) / 6
return u, t
u, t = rk4(V, np.array([1., 0., 1. , 0.]) , 0. , 10. , 100000)
x,y, vx,vy = u.T
# plt.plot(t, x, t,y)
plt.semilogy(t, x, t,y)
plt.grid('on')
print("Execution time:",time.clock() - start_time, "seconds")
plt.show()

Related

how do i create a graph of multiple x,y points inside a loop?

so I saw this code for RK4 on stack and I found it very useful. However, I cannot figure out a way to plot for each y value at each increment(h) of x.
def f(x,y):
return 2*x**2-4*x+y
def RK4(x0,y0):
while x0 < b:
k1 = h*f(x0,y0)
k2 = h*f(x0+0.5*h,y0+0.5*k1)
k3 = h*f(x0+0.5*h,y0+0.5*k2)
k4 = h*f(x0+h,y0+k3)
y0+=(k1+2*k2+2*k3+k4)/6
x0+=h
return y0
b=3
h=0.001
print(RK4(1,0.7182818))
You can append each point in a list as a tuple, and then perform the line plot operation on the list of tuples. You can find it in the commented code below.
import matplotlib.pyplot as plt
def f(x, y):
return 2 * x ** 2 - 4 * x + y
def RK4(x0, y0):
pts = [] # empty list
while x0 < b:
k1 = h * f(x0, y0)
k2 = h * f(x0 + 0.5 * h, y0 + 0.5 * k1)
k3 = h * f(x0 + 0.5 * h, y0 + 0.5 * k2)
k4 = h * f(x0 + h, y0 + k3)
y0 += (k1 + 2 * k2 + 2 * k3 + k4) / 6
x0 += h
pts.append((x0, y0)) # appending the tuple
plt.plot(*zip(*pts)) # plotting the list of tuple
plt.show()
return y0
b = 3
h = 0.001
print(RK4(1, 0.7182818))
You can see the plot as follows
From a design perspective, it would be preferred if the RK4 code and the plotting code were separated, the numerical solver should not be concerned with how its results are used afterwards.
Then the next decision would be about the construction of the time array, it could be passed to the RK4 method, or be constructed inside and returned, both have advantages. If speed is a concern, the arrays should be constructed explicitly in their final form (see example on math.SE), for expediency one can also construct them incrementally. Thus the code could be changed as
def RK4(f,x0,y0,xb,dx):
x, y = [x0],[y0]
while x0 < xb:
k1 = dx*f(x0,y0)
k2 = dx*f(x0+0.5*dx,y0+0.5*k1)
k3 = dx*f(x0+0.5*dx,y0+0.5*k2)
k4 = dx*f(x0+dx,y0+k3)
y0 += (k1+2*k2+2*k3+k4)/6
x0 += dx
x.append(x0); y.append(y0) # for vector y use y0.copy()
return x,y
and then call as
x,y = RK4(f=f,x0=1.0,y0=0.7182818,xb=3.0,dx=1e-3)
plt.plot(x,y)
#title, axis labels
plt.grid(); plt.show()

Not printing out the value of z and theta

I am solving two coupled ordinary differential equations using the 4th-order Runge-Kutta method. I am having trouble in printing the values of z as a result of applying this method. The source code is below for reference. Please, help me fix this code by printing the updated values of z and theta. Thank you.
#Import neeeded modules
import numpy as np
import matplotlib.pyplot as plt
#Input parameters
k = 5 #longitudinal torsional constant
delta = 10**-3 #longitudinal torsional constant
I = 10**-4 #Rotational Inertia
eps = 10**-2 #Coupling constant
m = 0.5
#Time Step
#Setting time array for graph visualization
dt = 0.002 #Time Step
tStop = 0.30 #Maximum time for graph visualization derived from Kinematics
t = np.arange(0., tStop+dt, dt) #Array of time
z = np.zeros(len(t))
dz = np.zeros(len(t))
theta = np.zeros(len(t))
dtheta = np.zeros(len(t))
#Functions that include the equations of motion
def dYdt(t,u):
z, dz, theta, dtheta = u
ddz = -(k*z+0.5*eps*theta)/m
ddtheta = -(delta*theta+0.5*eps*z)/I
return np.array([dz, ddz, dtheta, ddtheta])
def rk4(t, u, dt):
for i in range(len(t)-1):
# runge_kutta
k1 = dYdt(t[i], u[i])
k2 = dYdt(t[i] + dt/2, u[i] + dt/2 * k1)
k3 = dYdt(t[i] + dt/2, u[i] + dt/2 * k2)
k4 = dYdt(t[i] + dt, u[i] + dt * k3)
u.append(u[i] + (k1 + 2*k2 + 2*k3 + k4) * dt / 6)
#Unpacking
z, dz, theta, dtheta = np.asarray(u).T
print(z)
Here are the equations of motions I used to come up with the source code. Coupled ODEs
This is what I THINK you want, but I don't know what parameters to pass to rk4. Also, u is a 1D vector, so I don't know what you were expecting z, dz, theta, dtheta = np.asarray(u).T to do. As a result, this code won't run, but it will show you a potential design.
import numpy as np
import matplotlib.pyplot as plt
#Input parameters
k = 5 #longitudinal torsional constant
delta = 10**-3 #longitudinal torsional constant
I = 10**-4 #Rotational Inertia
eps = 10**-2 #Coupling constant
m = 0.5
#Time Step
#Setting time array for graph visualization
dt = 0.002 #Time Step
tStop = 0.30 #Maximum time for graph visualization derived from Kinematics
t = np.arange(0., tStop+dt, dt) #Array of time
#Functions that include the equations of motion
def dYdt(t,u):
z, dz, theta, dtheta = u
ddz = -(k*z+0.5*eps*theta)/m
ddtheta = -(delta*theta+0.5*eps*z)/I
return np.array([dz, ddz, dtheta, ddtheta])
def rk4(t, u, dt):
for i in range(len(t)-1):
# runge_kutta
k1 = dYdt(t[i], u[i])
k2 = dYdt(t[i] + dt/2, u[i] + dt/2 * k1)
k3 = dYdt(t[i] + dt/2, u[i] + dt/2 * k2)
k4 = dYdt(t[i] + dt, u[i] + dt * k3)
u.append(u[i] + (k1 + 2*k2 + 2*k3 + k4) * dt / 6)
#Unpacking
return np.asarray(u).T
z, dz, theta, dtheta = rk4(t, u, dt)
print(z)

solve_ivp returning different outcome of odeint?

I am trying to solve a simple ODE so as to understand the new API of Scipy.
I wrote a routine for Runge Kutta of order 4 to write it and confirmed it with the old API odeint and it matched beautifully. But now that I am trying to get around the solve_ivp, it seems that is not working. What am I getting wrong?
import numpy as np
from matplotlib import pyplot as plt
from scipy.integrate import solve_ivp, odeint
import time
freq = np.arange(1, 10000, 100)
def g(q, t):
return -q ** 3 + np.sin(t)
a = 0
b = 10
npoints = 100
h = (b - a) / npoints
t = np.arange(a, b, h)
output1 = np.zeros(t.shape)
x = 0
for i in range(len(t)):
output1[i] = x
k1 = h * g(x, t[i])
k2 = h * g(x + 0.5 * k1, t[i] + 0.5 * h)
k3 = h * g(x + 0.5 * k2, t[i] + 0.5 * h)
k4 = h * g(x + k3, t[i] + 0.5 * h)
x = x + 1 / 6 * (k1 + 2 * k2 + 2 * k3 + k4)
# ---------------Solving using odeint (old API)---------------#
y1_odeint = odeint(g, 0, t)
#---------------Solving using new API-------------#
y2=solve_ivp(g,(a,b),[0],t_eval=t)
# --------------------Representação gráfica--------------------------#
fig = plt.figure()
ax = fig.add_subplot(121)
ax1=fig.add_subplot(122)
ax.plot(t, output1,label="my own")
ax.plot(t,y1_odeint,label="odeint")
ax.plot(y2.t,np.squeeze(y2.y),label="new API")
ax.legend()
ax.set_title("Output")
ax1.plot(t,output1-np.squeeze(y1_odeint),label="|odeint-my own|")
ax1.legend()
plt.tight_layout()
plt.show()
Take another look at the docstring for solve_ivp. It expects the first argument of g to be t. By default, odeint uses the opposite convention. If you have a recent version of scipy, you can tell odeint that the first argument is t by giving it the argument tfirst=True.

how to solve IndexError: list assignment index out of range error?

I wrote a code for solving ode using rk4 method.
Here is the code:
from __future__ import division
import math
import numpy as np
from math import exp
def f(t,y):
return 5*t**2-y/math.e**(t+y)
def rk4(t,y,h,i):
p=[0]*i
p[0] = y
for n in range(0,i):
g=11
k1=round(h*f((t+n*h),y),g)
k2=round(h*f((t+n*h)+(h/2),y+k1/2),g)
k3=round(h*f((t+n*h)+h/2,y+k2/2),g)
k4=round(h*f((t+n*h)+h,y+k3),g)
p[n+1]=p[n]+(1/6)*(k1+2*k2+2*k3+k4)
print(k1,k2,k3,k4)
a=rk4(0,1,0.1,3)
print(a)
When I try to print the k's coefficient,it shows list assignment index out of range error and when I try to print the value of p,output is
[1, 0.9666652135433333, 0]
I don't understand why the third item is 0.
How do I solve these problems?
I improve your code snippet:
from __future__ import division
import math
def f(t, y):
return 5 * t**2 - y/math.e**(t + y)
def rk4(t, y, h, i):
p = list()
p.append(y)
for n in range(0, i):
g = 11
k1 = round(h * f((t + n * h), y), g)
k2 = round(h * f((t + n * h) + (h/2), y + k1/2), g)
k3 = round(h * f((t + n * h) + (h/2), y + k2/2), g)
k4 = round(h * f((t + n * h) + h, y + k3), g)
p.append(p[n] + (1/6) * (k1 + 2 * k2 + 2 * k3 + k4))
print(k1, k2, k3, k4)
rk4(0, 1, 0.1, 3)
Out:
(-0.03678794412, -0.03373778195, -0.03373873966, -0.0282677314)
(-0.02828710837, -0.02041047991, -0.02041201684, -0.01011306051)
(-0.01011942119, 0.00259988829, 0.0025995445, 0.01774691262)

RK4 Python Explanation

I'd like to use an implementation of RK4 I found online for something, but I'm having a bit of difficulty wrapping my head around the implementations I have found online.
For example:
def rk4(f, x0, y0, x1, n):
vx = [0] * (n + 1)
vy = [0] * (n + 1)
h = (x1 - x0) / float(n)
vx[0] = x = x0
vy[0] = y = y0
for i in range(1, n + 1):
k1 = h * f(x, y)
k2 = h * f(x + 0.5 * h, y + 0.5 * k1)
k3 = h * f(x + 0.5 * h, y + 0.5 * k2)
k4 = h * f(x + h, y + k3)
vx[i] = x = x0 + i * h
vy[i] = y = y + (k1 + k2 + k2 + k3 + k3 + k4) / 6
return vx, vy
Could someone please help me understand what exactly the parameters are? If possible, I'd like a more general explanation, but, if being more specific makes it easier to explain, I'm going to be using it specifically for an ideal spring system.
You are asking for the parameters here:
def rk4(f, x0, y0, x1, n):
...
return vx, vy
f is the ODE function, declared as def f(x,y) for the differential equation y'(x)=f(x,y(x)),
(x0,y0) is the initial point and value,
x1 is the end of the integration interval [x0,x1]
n is the number of sub-intervals or integration steps
vx,vx are the computed sample points, vy[k] approximates y(vx[k]).
You can not use this for the spring system, as that code only works for a scalar v. You would need to change it to work with numpy for vector operations.
def rk4(func, x0, y0, x1, n):
y0 = np.array(y0)
f = lambda x,y: np.array(func(x,y))
vx = [0] * (n + 1)
vy = np.zeros( (n + 1,)+y0.shape)
h = (x1 - x0) / float(n)
vx[0] = x = x0
vy[0] = y = y0[:]
for i in range(1, n + 1):
k1 = h * f(x, y)
k2 = h * f(x + 0.5 * h, y + 0.5 * k1)
k3 = h * f(x + 0.5 * h, y + 0.5 * k2)
k4 = h * f(x + h, y + k3)
vx[i] = x = x0 + i * h
vy[i] = y = y + (k1 + 2*(k2 + k3) + k4) / 6
return vx, vy

Categories

Resources