如何使用Runge-Kutta方法求解Lorenz 96模型?
How to solve Lorenz 96 model using Runge–Kutta method?
我写了下面的代码用Runge-Kutta方法求解Lorenz 96模型,但是结果不可靠如下图所示:
三个变量的正确关系如下:
如何修改代码才能正确解决问题?有关洛伦兹 96 的更多信息,请参阅 Lorenz 96 model- Wikipedia
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
###############################################################################
# Define Lorenz 96 function
# These are our constants
N = 3 # Number of variables
F = 8 # Forcing
def L96(v, t):
"""Lorenz 96 model with constant forcing"""
# Setting up vector
dv_dt = np.zeros(N)
# Loops over indices (with operations and Python underflow indexing handling edge cases)
for i in range(N):
dv_dt[i] = (v[(i + 1) % N] - v[i - 2]) * v[i - 1] - v[i] + F
return dv_dt
#define the given range t
t0=0
tn=100
h=0.005
#define number of steps (n)
time_range=np.arange(t0, tn, h)
# preallocate space
v0=np.zeros((len(time_range)+1, 1, N))
t=np.zeros(len(time_range)+1)
v0[0][0][0] += 0.01 # Add small perturbation to the first variable
L96(v0[0][0],0)
# Solve Runge-Kutta
for i in range (len(time_range)):
print(i)
dv_dt=L96(v0[i][0],t[i])
k1=dv_dt[0]
l1=dv_dt[1]
m1=dv_dt[2]
k2=L96([v0[i][0][0]+0.5*k1*h,v0[i][0][1]+0.5*l1*h,v0[i][0][2]+0.5*m1*h],t[i]+h/2)
l2=L96([v0[i][0][0]+0.5*k1*h,v0[i][0][1]+0.5*l1*h,v0[i][0][2]+0.5*m1*h],t[i]+h/2)
m2=L96([v0[i][0][0]+0.5*k1*h,v0[i][0][1]+0.5*l1*h,v0[i][0][2]+0.5*m1*h],t[i]+h/2)
k3=L96([v0[i][0][0]+0.5*k2[0]*h,v0[i][0][1]+0.5*l2[0]*h,v0[i][0][2]+0.5*m2[0]*h],t[i]+h/2)
l3=L96([v0[i][0][0]+0.5*k2[1]*h,v0[i][0][1]+0.5*l2[1]*h,v0[i][0][2]+0.5*m2[1]*h],t[i]+h/2)
m3=L96([v0[i][0][0]+0.5*k2[2]*h,v0[i][0][1]+0.5*l2[2]*h,v0[i][0][2]+0.5*m2[2]*h],t[i]+h/2)
k4=L96([v0[i][0][0]+0.5*k3[0]*h,v0[i][0][1]+0.5*l3[0]*h,v0[i][0][2]+0.5*m3[0]*h],t[i]+h/2)
l4=L96([v0[i][0][0]+0.5*k3[1]*h,v0[i][0][1]+0.5*l3[1]*h,v0[i][0][2]+0.5*m3[1]*h],t[i]+h/2)
m4=L96([v0[i][0][0]+0.5*k3[2]*h,v0[i][0][1]+0.5*l3[2]*h,v0[i][0][2]+0.5*m3[2]*h],t[i]+h/2)
v0[i+1][0][0] = v0[i][0][0] + h*(k1 +2*k2[0] +2*k3[0] +k4[0])/6 # final equations
v0[i+1][0][1] = v0[i][0][1] + h*(l1 +2*k2[1] +2*k3[1] +k4[1])/6
v0[i+1][0][2] = v0[i][0][2] + h*(m1+2*k2[2] +2*k3[2] +k4[2])/6
t[i+1]=time_range[i]
###############################################################################
v_array=np.array(v0)
v_array.shape
v1=v_array[:,0][:,0]
v2=v_array[:,0][:,1]
v3=v_array[:,0][:,2]
fig, (ax1, ax2) = plt.subplots(1, 2,constrained_layout=True,figsize=(10, 4))
ax1.plot(v1,v3)
ax1.set_title('v1 vs v2')
ax2.plot(v2,v3)
ax2.set_title('v2 vs v3')
# Plot 3d plot of v1,v2, and v3
from mpl_toolkits import mplot3d
fig = plt.figure(figsize=(8, 5))
ax = plt.axes(projection='3d', elev=40, azim=-50)
ax.plot3D(v1, v2, v3)
ax.set_xlabel('$v1$')
ax.set_ylabel('$v2$')
ax.set_zlabel('$v3$')
plt.show()
我不太确定您将矢量化再次分解为各个组件的行的动机是什么。无论如何,这是错误的。你的时间循环应该像
一样简单
for i in range(len(time)-1):
v[i+1] = RK4_step(lambda t,y:L96(y,t),time[i],v[i],time[i+1]-time[i])
其中方法步进器只是食谱代码
def RK4_step(f,t,y,h):
k1 = h*f(t,y)
k2 = h*f(t+0.5*h, y+0.5*k1)
k3 = h*f(t+0.5*h, y+0.5*k2)
k4 = h*f(t+h, y+k3)
return y+(k1+2*k2+2*k3+k4)/6
因此没有对 3 个组件的硬编码限制,代码组件是通用的,L96
函数可以与库中的任意数量的数值求解器或自行实现的、步进器和时间一起使用RK4 循环适用于任何其他微分方程组。
请注意,在维基百科示例中,有 N=5
个组件,其中 3D 图是从其中的前 3 个组件构建的。对于 N=3
你确实收敛到一个看起来像直线的固定点,对于 N=4
它看起来好像有一个极限环,只有对于 N=5
解开始看起来混乱.
如果一次计算多个轨迹,则使用 3 维数组很有意义。 (但只能使用固定步长方法,或者如果保证它们靠得很近,否则大多数时候步长控制将不适合大多数轨迹。)该改编的完整脚本如下:
# Define Lorenz 96 function
# These are our constants
N = 4 # Number of variables
F = 8 # Forcing
def L96(t, v):
"""Lorenz 96 model with constant forcing"""
# Setting up vector
dv_dt = 0*v
# Loops over indices (with operations and Python underflow indexing handling edge cases)
for i in range(N):
dv_dt[i] = (v[(i + 1) % N] - v[i - 2]) * v[i - 1] - v[i] + F
return dv_dt
#define the given range t
t0=0
tn=100
h=0.005
#define number of steps (n)
time = np.arange(t0, tn, h)
v = np.zeros([len(time),N,3], float)
v[0] +=F
v[0,0,0] -= 0.005
v[0,0,1] += 0.005
v[0,0,2] += 0.01
for i in range(len(time)-1):
v[i+1] = RK4_step(L96,time[i],v[i],time[i+1]-time[i])
# Plot 3d plot of first 3 coordinates
from mpl_toolkits import mplot3d
fig = plt.figure(figsize=(8, 5))
ax = plt.axes(projection='3d', elev=40, azim=-50)
ax.plot3D(v[:,0,0], v[:,1,0], v[:,2,0],'g', lw=0.2)
ax.plot3D(v[:,0,1], v[:,1,1], v[:,2,1],'r', lw=0.2)
ax.plot3D(v[:,0,2], v[:,1,2], v[:,2,2],'b', lw=0.2)
ax.set_xlabel('$v_1$')
ax.set_ylabel('$v_2$')
ax.set_zlabel('$v_3$')
ax.set_title(f"N={N}")
plt.show()
我写了下面的代码用Runge-Kutta方法求解Lorenz 96模型,但是结果不可靠如下图所示:
三个变量的正确关系如下:
如何修改代码才能正确解决问题?有关洛伦兹 96 的更多信息,请参阅 Lorenz 96 model- Wikipedia
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
###############################################################################
# Define Lorenz 96 function
# These are our constants
N = 3 # Number of variables
F = 8 # Forcing
def L96(v, t):
"""Lorenz 96 model with constant forcing"""
# Setting up vector
dv_dt = np.zeros(N)
# Loops over indices (with operations and Python underflow indexing handling edge cases)
for i in range(N):
dv_dt[i] = (v[(i + 1) % N] - v[i - 2]) * v[i - 1] - v[i] + F
return dv_dt
#define the given range t
t0=0
tn=100
h=0.005
#define number of steps (n)
time_range=np.arange(t0, tn, h)
# preallocate space
v0=np.zeros((len(time_range)+1, 1, N))
t=np.zeros(len(time_range)+1)
v0[0][0][0] += 0.01 # Add small perturbation to the first variable
L96(v0[0][0],0)
# Solve Runge-Kutta
for i in range (len(time_range)):
print(i)
dv_dt=L96(v0[i][0],t[i])
k1=dv_dt[0]
l1=dv_dt[1]
m1=dv_dt[2]
k2=L96([v0[i][0][0]+0.5*k1*h,v0[i][0][1]+0.5*l1*h,v0[i][0][2]+0.5*m1*h],t[i]+h/2)
l2=L96([v0[i][0][0]+0.5*k1*h,v0[i][0][1]+0.5*l1*h,v0[i][0][2]+0.5*m1*h],t[i]+h/2)
m2=L96([v0[i][0][0]+0.5*k1*h,v0[i][0][1]+0.5*l1*h,v0[i][0][2]+0.5*m1*h],t[i]+h/2)
k3=L96([v0[i][0][0]+0.5*k2[0]*h,v0[i][0][1]+0.5*l2[0]*h,v0[i][0][2]+0.5*m2[0]*h],t[i]+h/2)
l3=L96([v0[i][0][0]+0.5*k2[1]*h,v0[i][0][1]+0.5*l2[1]*h,v0[i][0][2]+0.5*m2[1]*h],t[i]+h/2)
m3=L96([v0[i][0][0]+0.5*k2[2]*h,v0[i][0][1]+0.5*l2[2]*h,v0[i][0][2]+0.5*m2[2]*h],t[i]+h/2)
k4=L96([v0[i][0][0]+0.5*k3[0]*h,v0[i][0][1]+0.5*l3[0]*h,v0[i][0][2]+0.5*m3[0]*h],t[i]+h/2)
l4=L96([v0[i][0][0]+0.5*k3[1]*h,v0[i][0][1]+0.5*l3[1]*h,v0[i][0][2]+0.5*m3[1]*h],t[i]+h/2)
m4=L96([v0[i][0][0]+0.5*k3[2]*h,v0[i][0][1]+0.5*l3[2]*h,v0[i][0][2]+0.5*m3[2]*h],t[i]+h/2)
v0[i+1][0][0] = v0[i][0][0] + h*(k1 +2*k2[0] +2*k3[0] +k4[0])/6 # final equations
v0[i+1][0][1] = v0[i][0][1] + h*(l1 +2*k2[1] +2*k3[1] +k4[1])/6
v0[i+1][0][2] = v0[i][0][2] + h*(m1+2*k2[2] +2*k3[2] +k4[2])/6
t[i+1]=time_range[i]
###############################################################################
v_array=np.array(v0)
v_array.shape
v1=v_array[:,0][:,0]
v2=v_array[:,0][:,1]
v3=v_array[:,0][:,2]
fig, (ax1, ax2) = plt.subplots(1, 2,constrained_layout=True,figsize=(10, 4))
ax1.plot(v1,v3)
ax1.set_title('v1 vs v2')
ax2.plot(v2,v3)
ax2.set_title('v2 vs v3')
# Plot 3d plot of v1,v2, and v3
from mpl_toolkits import mplot3d
fig = plt.figure(figsize=(8, 5))
ax = plt.axes(projection='3d', elev=40, azim=-50)
ax.plot3D(v1, v2, v3)
ax.set_xlabel('$v1$')
ax.set_ylabel('$v2$')
ax.set_zlabel('$v3$')
plt.show()
我不太确定您将矢量化再次分解为各个组件的行的动机是什么。无论如何,这是错误的。你的时间循环应该像
一样简单for i in range(len(time)-1):
v[i+1] = RK4_step(lambda t,y:L96(y,t),time[i],v[i],time[i+1]-time[i])
其中方法步进器只是食谱代码
def RK4_step(f,t,y,h):
k1 = h*f(t,y)
k2 = h*f(t+0.5*h, y+0.5*k1)
k3 = h*f(t+0.5*h, y+0.5*k2)
k4 = h*f(t+h, y+k3)
return y+(k1+2*k2+2*k3+k4)/6
因此没有对 3 个组件的硬编码限制,代码组件是通用的,L96
函数可以与库中的任意数量的数值求解器或自行实现的、步进器和时间一起使用RK4 循环适用于任何其他微分方程组。
请注意,在维基百科示例中,有 N=5
个组件,其中 3D 图是从其中的前 3 个组件构建的。对于 N=3
你确实收敛到一个看起来像直线的固定点,对于 N=4
它看起来好像有一个极限环,只有对于 N=5
解开始看起来混乱.
如果一次计算多个轨迹,则使用 3 维数组很有意义。 (但只能使用固定步长方法,或者如果保证它们靠得很近,否则大多数时候步长控制将不适合大多数轨迹。)该改编的完整脚本如下:
# Define Lorenz 96 function
# These are our constants
N = 4 # Number of variables
F = 8 # Forcing
def L96(t, v):
"""Lorenz 96 model with constant forcing"""
# Setting up vector
dv_dt = 0*v
# Loops over indices (with operations and Python underflow indexing handling edge cases)
for i in range(N):
dv_dt[i] = (v[(i + 1) % N] - v[i - 2]) * v[i - 1] - v[i] + F
return dv_dt
#define the given range t
t0=0
tn=100
h=0.005
#define number of steps (n)
time = np.arange(t0, tn, h)
v = np.zeros([len(time),N,3], float)
v[0] +=F
v[0,0,0] -= 0.005
v[0,0,1] += 0.005
v[0,0,2] += 0.01
for i in range(len(time)-1):
v[i+1] = RK4_step(L96,time[i],v[i],time[i+1]-time[i])
# Plot 3d plot of first 3 coordinates
from mpl_toolkits import mplot3d
fig = plt.figure(figsize=(8, 5))
ax = plt.axes(projection='3d', elev=40, azim=-50)
ax.plot3D(v[:,0,0], v[:,1,0], v[:,2,0],'g', lw=0.2)
ax.plot3D(v[:,0,1], v[:,1,1], v[:,2,1],'r', lw=0.2)
ax.plot3D(v[:,0,2], v[:,1,2], v[:,2,2],'b', lw=0.2)
ax.set_xlabel('$v_1$')
ax.set_ylabel('$v_2$')
ax.set_zlabel('$v_3$')
ax.set_title(f"N={N}")
plt.show()