点击上方「蓝字」关注我们
在数学与工程领域中,常微分方程(ODEs)是描述动态系统行为的重要工具。从物理系统的动力学模型到化学反应的动力学过程,常微分方程无处不在。然而,许多实际问题中的常微分方程往往难以获得解析解,这促使人们发展了各种数值方法来近似求解这些方程。
我们在前面已经介绍了一阶常微分方程初值问题的若干解法,若作进一步推广,如求解一阶方程组初值问题,或者高阶方程的初值问题,又该如何分析呢?本文将给出相关的答案。
一阶方程组初值问题
为简单起见,设有 2 个一阶方程组成的方程组初值问题:
上述方程可以被看作是一阶向量方程的初值问题:
其中,
且
于是,可以将原标量型一阶方程初值问题的数值解法(如欧拉方法、龙格-库塔方法等),直接应用于上述向量形式的方程,这样就得到了一阶方程组初值问题的数值解法。
例如,如果对式 (2) 采用欧拉方法,则其迭代公式为
也可写成分量形式,即
同样,若对式 (2) 采用经典的四阶龙格-库塔方法,则其迭代公式为:
其中,
对于由 个方程构成的方程组的情形也有类似的讨论,只是向量的分量个数为 而已。
高阶方程初值问题
对于高阶常微分方程,可以通过引入新的变量将其转化为一阶方程组的形式。
先考察如下的二阶方程初值问题:
通过引入新的变量 ,则有 ,就可将上述方程转化为形如式(3)的一阶方程组初值问题:
类似地,对于更高阶的方程,如 阶方程初值问题:
按照同样的思路,可以定义 ,,,,,,可以将原问题转化为以下一阶方程组的初值问题:
此式即为 情况下的式(2)。
例如,对于三阶方程初值问题
令 ,,,则可将原三阶方程问题转化为如下一阶方程组的初值问题:
这样,就可以采用一阶方程组初值问题的数值方法进行求解了。
例题
用经典的四阶龙格-库塔法求解一阶方程组初值问题:
取步长为 0.1。已知该方程组的精确解为 ,。
Fortran 实现
利用 Fortran 语言,我们可以高效地实现该问题的数值解法。为了便于比较数值算法的效果,我们同时给出原方程的精确解。具体方法参见以下的代码和注释:
program main
implicit none
integer :: N, i
integer, parameter :: nvar = 2
real(kind=8) :: a, b, h
real(kind=8), dimension(nvar) :: k1, k2, k3, k4, ye, err
real(kind=8), allocatable, dimension(:) :: x
real(kind=8), allocatable, dimension(:,:) :: 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.0D0, 0.0D0] !初值
err = 0.0D0
ye = yexact(x(1), nvar)
!打开存储文件
open(10, file="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)
integer, intent(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)
integer, intent(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 语言来实现这一方法。对于高阶方程,本质上可以利用降阶的思想,将高阶问题转化为一阶方程组的问题,然后再进行求解。无论是简单的欧拉方法还是更为精确的龙格-库塔方法,都可以用来求解此类问题。随着计算机技术的发展,这些数值方法的应用范围也将不断扩大,为科学研究和工程技术带来极大的便利。
往期推荐
推荐阅读
FEtch 系统是笔者团队开发的新一代有限元软件开发平台。只需按照有限元语言格式填写脚本文件,即可在线自动生成基于现代 Fortran 的有限元计算程序,从而大幅提高 CAE 软件的开发效率。欢迎私信交流。
有任何疑问或建议,欢迎加Q群 "FEtch有限元开发系统(519166061)" 留言讨论。我们长期开展 FEtch 系统的试用活动,感兴趣的朋友入群后可直接联系管理员,免费获取许可证文件。