【问题标题】:How to solve Lorenz 96 model using Runge–Kutta method?如何使用 Runge-Kutta 方法求解 Lorenz 96 模型?
【发布时间】:2020-12-21 08:22:31
【问题描述】:

我已经编写了下面的代码来使用 Runge-Kutta 方法求解 Lorenz 96 模型,但结果并不可靠,如下图所示:

三个变量的正确关系如下:

如何修改代码以正确解决问题?有关 Lorenz 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()

【问题讨论】:

    标签: python differential-equations runge-kutta lorenz-system


    【解决方案1】:

    我不确定您将矢量化再次拆分为单个组件的行的动机是什么。无论如何,这是错误的。你的时间循环应该很简单

    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 的步进器和时间循环是适用于任何其他微分方程组。

    请注意,在 wikipedia 示例中,有 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()
    

    【讨论】:

    • 感谢您的回答。如果您检查 Lorenz 96 描述,它是 N 个微分方程组,其中 N 是变量数。因此,在每次迭代中,L96 函数都会产生一个包含 N 个值的向量,Runge-Kutta 方法应该考虑这些值来解决问题。我运行了你的代码,但它不起作用,因为认为向量 v 在每个循环中只有一个值,不能用作 L96 函数的输入。
    • 在用 k4 纠正复制粘贴错误后效果很好,对于 N=3,系统收敛到一个固定点,对于 N=4,它似乎以周期性循环结束,对于N=5 解决方案看起来很混乱。
    • 再次感谢您的回答。我稍微修改了您的代码,并将完整的代码放在您的答案中,以方便读者轻松理解。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-02
    • 2014-07-27
    • 2011-07-25
    • 2021-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多