python初探常微分方程组数值解

预备知识

包括最常用的四阶Ronge-Kutta数值方法以及四阶Adams预估-校正格式

四阶R-K

之所以是四阶R-K,是因为三阶精度太低,在步长略大时无法满足正常求解精度要求,而五阶以上虽然精度很高,但算法耗时。四阶R-K较为折中,既可满足精度要求,对于复杂计算其计算速度也可接受。
四阶R-K公式的标准格式如下:
对于形如   d y d x = f ( x , y ) \ \frac{dy}{dx}=f(x,y)  dxdy=f(x,y) 的常微分方程:

  y i + 1 = y i + h 6 ( K 1 + 2 K 2 + 2 K 3 + K 4 )   K 1 = f ( x i , y i )   K 2 = f ( x i + h 2 , y i + h 2 K 1 )   K 3 = f ( x i + h 2 , y i + h 2 K 2 )   K 4 = f ( x i + h , y i + h K 3 ) (1) \ y_{i+1}=y_i+\frac{h}{6}(K_1+2K_2+2K_3+K_4)\\ \ K_1=f(x_i,y_i)\\ \ K_2=f(x_i+\frac{h}{2},y_i+\frac{h}{2}K_1)\\ \tag{1} \ K_3=f(x_i+\frac{h}{2},y_i+\frac{h}{2}K_2)\\ \ K_4=f(x_i+h,y_i+hK_3)  yi+1=yi+6h(K1+2K2+2K3+K4) K1=f(xi,yi) K2=f(xi+2h,yi+2hK1) K3=f(xi+2h,yi+2hK2) K4=f(xi+h,yi+hK3)(1)

对于常微分方程组来说,将   y \ y  y   f \ f  f 向量化即可,其中   h \ h  h 为步长,应尽量取小。

四阶Adams预估-校正公式

四阶Adams预估-校正格式可以看做一种改进的Euler格式,其计算效率相较四阶R-K更高,但其精度不如四阶R-K,在各种工程应用中应做出各种尝试后选取适合实际的算法。
四阶Adams预估-校正公式标准格式如下:
对于形如   d y d x = f ( x , y ) \ \frac{dy}{dx}=f(x,y)  dxdy=f(x,y) 的常微分方程:

预估:   y ˉ i + 1 = y i + h 24 ( 55 f i − 59 f i − 1 + 37 f i − 2 − 9 f i − 3 ) \ \bar{y}_{i+1}=y_i+\frac{h}{24}(55f_i-59f_{i-1}+37f_{i-2}-9f_{i-3})  yˉi+1=yi+24h(55fi59fi1+37fi29fi3)
校正:   y i + 1 = h 24 ( 9 f ( x i + 1 , y ˉ i + 1 ) + 19 f i − 5 f i − 1 + f i − 2 ) (2) \ y_{i+1}=\frac{h}{24}(9f(x_{i+1},\bar{y}_{i+1})+19f_i-5f_{i-1}+f_{i-2}) \tag{2}  yi+1=24h(9f(xi+1,yˉi+1)+19fi5fi1+fi2)(2)
其中   i = 3 , 4 , 5... , n − 1 \ i=3,4,5...,n-1  i=3,4,5...,n1
由于在预估式中出现了   f i − 3 , f i − 2 , f i − 1 \ f_{i-3},f_{i-2},f_{i-1}  fi3,fi2,fi1,因此在启动Adams预估校正算法之前需要用其他算法进行前三个时间步的运算,较为普遍的是采用四阶R-K公式进行启动计算。

实战演练

理论推导

本文采用难度中等的常微分方程组进行演练,其物理含义为Apollo卫星的运动轨迹   ( x , y ) \ (x,y)  (x,y)
其满足以下方程组:
  x ¨ = 2 y ˙ + x − μ ∗ ( x + μ ) r 1 3 − μ ( x + μ ∗ ) r 2 3 y ¨ = − 2 x ˙ + y − μ ∗ y r 1 3 − μ y r 2 3 (2) \ \ddot{x}=2\dot{y}+x-\frac{\mu^*(x+\mu)}{{r_1}^3}-\frac{\mu(x+\mu^*)}{{r_2}^3}\\ \tag{2} \ddot{y}=-2\dot{x}+y-\frac{\mu^*y}{{r_1}^3}-\frac{\mu y}{{r_2}^3}  x¨=2y˙+xr13μ(x+μ)r23μ(x+μ)y¨=2x˙+yr13μyr23μy(2)
其中   μ = 1 / 82.45 , μ ∗ = 1 − μ r 1 = ( x + μ ) 2 + y 2 , r 2 = ( x − μ ∗ ) 2 + y 2 \ \mu=1/82.45, \mu^*=1-\mu\\ r_1=\sqrt{(x+\mu)^2+y^2},r_2=\sqrt{(x-\mu^*)^2+y^2}  μ=1/82.45,μ=1μr1=(x+μ)2+y2 ,r2=(xμ)2+y2
之后对常微分方程组进行变量代换,取   x 1 = x , x = x ˙ , x 3 = y , x 4 = y ˙ \ x_1=x,x=\dot{x},x_3=y,x_4=\dot{y}  x1=x,x=x˙,x3=y,x4=y˙
则式   ( 2 ) \ (2)  (2)可化为下式   ( 3 ) \ (3)  (3)
  x 1 ˙ = x 2   x 2 ˙ = 2 x 4 + x 1 − μ ∗ ( x 1 + μ ) / r 1 3 − μ ( x 1 − μ ∗ ) / r 2 3   x 3 ˙ = x 4   x 4 ˙ = − 2 x 2 + x 3 − μ ∗ x 3 / r 1 3 − μ x 3 / r 2 3 (3) \ \dot{x_1}=x_2\\ \ \dot{x_2}=2x_4+x_1-\mu^*(x_1+\mu)/{r_1}^3-\mu(x_1-\mu^*)/{r_2}^3\\ \ \dot{x_3}=x_4\\ \ \dot{x_4}=-2x_2+x_3-\mu^*x_3/{r_1}^3-\mu x_3/{r_2}^3 \tag{3}  x1˙=x2 x2˙=2x4+x1μ(x1+μ)/r13μ(x1μ)/r23 x3˙=x4 x4˙=2x2+x3μx3/r13μx3/r23(3)
同时由于式   ( 2 ) \ (2)  (2)的初始条件为
  x ( 0 ) = 1.2 , y ( 0 ) = 0 , x ˙ ( 0 ) = 0 , y ˙ ( 0 ) = − 1.04935751 \ x(0)=1.2,y(0)=0,\dot{x}(0)=0,\dot{y}(0)=-1.04935751  x(0)=1.2,y(0)=0,x˙(0)=0,y˙(0)=1.04935751
此时我们得到了常微分方程组,初始条件,利用四阶R-K公式,设定合适的步长即可对其进行求解。

python实现

下面对其python代码实现进行展示:

创建求解常微分方程组的简单类

# -*- coding: utf-8 -*-
"""
Created on Mon Jan 25 11:12:42 2021

@author: xinghy
"""
# 1.2  0  0  -1.04935751'''initial'''
import numpy as np

class Ode_crew():
    '''equation crew'''
    def __init__(self, x1, x2, x3, x4, t_initial, t_last, t_iter):    
        self.x1 = [x1]
        self.x2 = [x2]
        self.x3 = [x3]
        self.x4 = [x4]
        self.time_pool = [t_initial]
        self.t_latest = t_initial
        self.t_iter = t_iter
        self.t_last = t_last
        self.x = [self.x1, self.x2, self.x3, self.x4]
        
    def f(self, x1, x2, x3, x4):
    '''方程组'''
        x1_ = x2
        x2_ = 2 * x4 + x1 - (1 - 1/82.45)*(x1 + 1/82.45)/np.sqrt((x1 + 1/82.45)**2+x3**2)**3 - (
            1/82.45)*(x1 -1 + 1/82.45)/np.sqrt((x1 - 1 + 1/82.45)**2 + x3**2)**3
        x3_ = x4
        x4_ = -2 * x2 + x3 - (1 - 1/82.45)*x3/np.sqrt((x1 + 1/82.45)**2+x3**2)**3 - (
            1/82.45)*x3/np.sqrt((x1- 1 + 1/82.45)**2 + x3**2)**3
        f = [x1_, x2_, x3_, x4_]
        return f
    
    def four_RK(self):
    '''四阶R-K'''
        K1 = self.f(self.x1[-1], self.x2[-1], self.x3[-1], self.x4[-1])
        K2 = self.f(self.x1[-1] + self.t_iter/2*K1[0], self.x2[-1] + self.t_iter/2*K1[1],
                    self.x3[-1] + self.t_iter/2*K1[2], self.x4[-1] + self.t_iter/2*K1[3])
        K3 = self.f(self.x1[-1] + self.t_iter/2*K2[0], self.x2[-1] + self.t_iter/2*K2[1],
                    self.x3[-1] + self.t_iter/2*K2[2], self.x4[-1] + self.t_iter/2*K2[3])
        K4 = self.f(self.x1[-1] + self.t_iter*K3[0], self.x2[-1] + self.t_iter*K3[1],
                    self.x3[-1] + self.t_iter*K3[2], self.x4[-1] + self.t_iter*K3[3])
        x_ori = np.array([self.x1[-1], self.x2[-1], self.x3[-1], self.x4[-1]])
        x_new = x_ori + np.array(self.t_iter/6 * (np.array(K1) + 2*np.array(K2) + 2*
                                                  np.array(K3) + np.array(K4)))
        self.x1.append(x_new[0])
        self.x2.append(x_new[1])
        self.x3.append(x_new[2])
        self.x4.append(x_new[3])
        self.t_latest += self.t_iter
        self.time_pool.append(self.t_latest)

之后将各种条件代入即可用指定算法进行运算:

import numpy as np
import matplotlib.pyplot as plt

from ode import Ode_crew

odecrew = Ode_crew(1.2, 0, 0, -1.04935751, 0, 20, 0.001)
while odecrew.t_latest < odecrew.t_last - 0.0001*0.1:
    odecrew.four_RK()
    # print(odecrew.time_pool[-1])
x_ = odecrew.x[0]
y_ = odecrew.x[2]
plt.scatter(x_, y_, c=y_, cmap=plt.cm.Blues, s=0.2)
plt.title('Apollo trajectory', fontsize=25)
plt.xlabel('x', fontsize=10)
plt.ylabel('y', fontsize=10)

运行后即可得到如下图所示的Apollo运行轨迹图:
由图可以看到卫星的运行轨迹结果,同时添加了一些颜色渐变的效果,比较简陋,之后有时间可以精进一下。

附录

前面也提到了四阶Adams预估-校正公式,是一个与四阶R-K算法总是同时出现的常用算法,但笔者运算之后发现四阶Adams所需步长要远小于四阶R-K所需步长,其精度总是与四阶R-K差几个量级,可能是方程组的固有性质所致,也可能是四阶Adams的固有弊端,在这里就不展开Adams的结果了~

以上,请各位巨巨批评指正!

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值