常微分方程的数值求解 | 一阶方程组和高阶方程

学术   科技   2024-10-15 22:21   山东  

点击上方「蓝字」关注我们

 

在数学与工程领域中,常微分方程(ODEs)是描述动态系统行为的重要工具。从物理系统的动力学模型到化学反应的动力学过程,常微分方程无处不在。然而,许多实际问题中的常微分方程往往难以获得解析解,这促使人们发展了各种数值方法来近似求解这些方程。

我们在前面已经介绍了一阶常微分方程初值问题的若干解法,若作进一步推广,如求解一阶方程组初值问题,或者高阶方程的初值问题,又该如何分析呢?本文将给出相关的答案。

一阶方程组初值问题

为简单起见,设有 2 个一阶方程组成的方程组初值问题:

上述方程可以被看作是一阶向量方程的初值问题:

其中,

于是,可以将原标量型一阶方程初值问题的数值解法(如欧拉方法、龙格-库塔方法等),直接应用于上述向量形式的方程,这样就得到了一阶方程组初值问题的数值解法。

例如,如果对式 (2) 采用欧拉方法,则其迭代公式为

也可写成分量形式,即

同样,若对式 (2) 采用经典的四阶龙格-库塔方法,则其迭代公式为:

其中,

对于由 个方程构成的方程组的情形也有类似的讨论,只是向量的分量个数为 而已。

高阶方程初值问题

对于高阶常微分方程,可以通过引入新的变量将其转化为一阶方程组的形式。

先考察如下的二阶方程初值问题:

通过引入新的变量 ,则有 ,就可将上述方程转化为形如式(3)的一阶方程组初值问题:

类似地,对于更高阶的方程,如 阶方程初值问题:

按照同样的思路,可以定义 ,可以将原问题转化为以下一阶方程组的初值问题:

此式即为 情况下的式(2)。

例如,对于三阶方程初值问题

,则可将原三阶方程问题转化为如下一阶方程组的初值问题:

这样,就可以采用一阶方程组初值问题的数值方法进行求解了。

例题

用经典的四阶龙格-库塔法求解一阶方程组初值问题:

取步长为 0.1。已知该方程组的精确解为

Fortran 实现

利用 Fortran 语言,我们可以高效地实现该问题的数值解法。为了便于比较数值算法的效果,我们同时给出原方程的精确解。具体方法参见以下的代码和注释:

program main
  implicit none
  
  integer :: N, i
  integerparameter :: nvar = 2
  real(kind=8) :: a, b, h
  real(kind=8), dimension(nvar) :: k1, k2, k3, k4, ye, err
  real(kind=8), allocatabledimension(:) :: x
  real(kind=8), allocatabledimension(:,:) :: y
  
  a = 0.0D0
  b = 3.1D0
  h = 0.1D0  !步长
  N = nint((b - a)/h) !总的剖分数
  
  !动态分配长度为(N+1)的数组
  !x存放节点坐标
  !y存放对应节点的数值解
  allocate(x(N+1), y(N+1,nvar))
  
  !节点坐标
  x = [(a + i * h, i = 0, N)]
  
  y(1,:) = [1.0D00.0D0!初值
  err = 0.0D0
  ye = yexact(x(1), nvar)
  
  !打开存储文件
  open(10file="res.txt")
  write(10,*) "#    x(1)        y(1)     yexact(1)     y(2)      yexact(2)    err_max "
  !保存初值
  write(10,"(7e12.3)") x(1), y(1,1), ye(1), y(1,2), ye(2), maxval(err)

  do i = 1, N !4阶龙格-库塔法
    x(i+1) = x(i) + h 
    k1 = h*f(x(i), y(i,:), nvar)
    k2 = h*f(x(i) + 0.5D0*h, y(i,:) + 0.5D0*k1, nvar)
    k3 = h*f(x(i) + 0.5D0*h, y(i,:) + 0.5D0*k2, nvar)
    k4 = h*f(x(i+1), y(i,:) + k3, nvar)
    y(i+1,:) = y(i,:) + (k1 + 2D0 * k2 + 2D0 * k3 + k4) / 6.0D0
 ye = yexact(x(i+1), nvar) !计算精确解
    err = abs(y(i+1,:) - ye)  !计算节点处的误差
 !保存结果
   write(10,"(7e12.3)") x(i+1), y(i+1,1), ye(1), y(i+1,2), ye(2), maxval(err)
  end do
  close(10)
  
contains
  !右端项函数
  function f(x, y, nvar) result (res)
    integerintent(in) :: nvar  
    real(kind=8), intent(in) :: x, y(nvar)
    real(kind=8) :: res(nvar)
    res(1) = y(2)
    res(2) = -y(1
  end function f
  !精确解y
  function yexact(x, nvar) result (res)
    integerintent(in) :: nvar
    real(kind=8), intent(in) :: x
    real(kind=8) :: res(nvar)
    res(1) = cos(x)
    res(2) = -sin(x) 
  end function yexact
end program main

以上程序由原来的求解标量型常微分方程的程序修改而来,将部分变量改为了数组,变动不大。感兴趣的读者可自行对比:

常微分方程的数值求解 | 龙格-库塔法

编译并运行,得到的计算结果文件res.txt的内容如下。

观察发现,当以 0.1 为步长时,计算结果大概有 5~6 位的有效数字,可见四阶龙格-库塔方法精度还是相当高的。

Gnuplot 绘图

为了更好地展示计算结果,我们使用 Gnuplot 来读取 Fortran 程序的输出文件并绘图。在 Fortran 程序同一目录下,创建并运行以下 plt 脚本文件,就可以轻松得到 png 格式的图片文件。运行结果和对应的脚本代码如下:

set terminal png
set output "res.png"

set title "Runge-Kutta Method"
set xlabel "x"
set ylabel "y"
set key right top spacing 2

plot "res.txt" using 1:2 title "Numerical y1", \
"res.txt" using 1:3 title "Analytical y1" \
with lines linewidth 2, \
"res.txt" using 1:4 title "Numerical y2", \
"res.txt" using 1:5 title "Analytical y2" \
with lines linewidth 2

小结

本文主要介绍了一阶方程组初值问题数值求解的基本原理,并通过一个具体的例子展示了如何使用 Fortran 语言来实现这一方法。对于高阶方程,本质上可以利用降阶的思想,将高阶问题转化为一阶方程组的问题,然后再进行求解。无论是简单的欧拉方法还是更为精确的龙格-库塔方法,都可以用来求解此类问题。随着计算机技术的发展,这些数值方法的应用范围也将不断扩大,为科学研究和工程技术带来极大的便利。


往期推荐

常微分方程的数值求解 | 龙格-库塔法

常微分方程的数值求解 | 从欧拉方法启航

Gnuplot:数据可视化的理想工具



推荐阅读


FEtch 系统是笔者团队开发的新一代有限元软件开发平台。只需按照有限元语言格式填写脚本文件,即可在线自动生成基于现代 Fortran 的有限元计算程序,从而大幅提高 CAE 软件的开发效率。欢迎私信交流。

有任何疑问或建议,欢迎加Q群 "FEtch有限元开发系统(519166061)" 留言讨论。我们长期开展 FEtch 系统的试用活动,感兴趣的朋友入群后可直接联系管理员,免费获取许可证文件

喜欢作者,请点在看

有限元语言与编程
面向科学计算,探索CAE,有限元,数值分析,高性能计算,数据可视化,以及 Fortran、C/C++、Python、Matlab、Mathematica 等语言编程。这里提供相关的技术文档和咨询服务,不定期分享学习心得。Enjoy!
 最新文章