Files
goNum/RKF45_test.go
Black Ghost e9ac846d52 update comments
update comments in all of files in convenient to generate package information using `godoc` command or give tips in LiteIDE editor when the cursor keeps resting on a function or method.
2019-03-01 10:13:34 +08:00

209 lines
6.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// RKF45_test
/*
------------------------------------------------------
作者 : Black Ghost
日期 : 2018-12-19
版本 : 0.0.0
------------------------------------------------------
四级五阶变步长Runge-Kutta法求解常微分方程组
理论:
参考 John H. Mathews and Kurtis D. Fink. Numerical
methods using MATLAB, 4th ed. Pearson
Education, 2004. ss 9.5.4.
------------------------------------------------------
输入 :
fun 第i个方程(计算变量值向量, i)
x0 初值向量,(fn+1)x1一个xfn个因变量
xend 终止x
tol 步长控制误差
fn 方程个数
n 最大迭代步数
输出 :
sol 解向量
err 解出标志false-未解出或达到步数上限;
true-全部解出
------------------------------------------------------
*/
package goNum_test
import (
"math"
"testing"
"github.com/chfenger/goNum"
)
// RKF45 四级五阶变步长Runge-Kutta法求解常微分方程组
func RKF45(fun func(goNum.Matrix, int) float64, x0 goNum.Matrix,
xend, tol float64, fn, n int) (goNum.Matrix, bool) {
/*
四级五阶变步长Runge-Kutta法求解常微分方程组
输入 :
fun 第i个方程(计算变量值向量, i)
x0 初值向量,(fn+1)x1一个xfn个因变量
xend 终止x
tol 步长控制误差
fn 方程个数
n 最大迭代步数
输出 :
sol 解向量
err 解出标志false-未解出或达到步数上限;
true-全部解出
*/
//判断方程个数是否对应初值个数
if x0.Rows != fn+1 {
panic("Error in goNum.RKF45: Quantities of x0 and fn+1 are not equal")
}
//判断tol值
if tol <= 0.0 {
panic("Error in goNum.RKF45: tol less than or euqals to zero")
}
//判断xend值
if xend <= x0.Data[0] {
panic("Error in goNum.RKF45: xend less than or euqals to x0")
}
sol0 := goNum.ZeroMatrix(fn+1, n+1)
var err bool = false
h := 100.0 * (xend - x0.Data[0]) / float64(n) //初始步长100倍最小步长可修改
//把初值赋给sol
for i := 0; i < fn+1; i++ {
sol0.SetMatrix(i, 0, x0.Data[i])
}
//nreal解矩阵实际长度
var i, nreal int = 1, 1
for sol0.GetFromMatrix(0, i-1) < xend { //最大迭代次数控制
temp0 := goNum.ZeroMatrix(fn+1, 1)
//给temp0赋i-1步值每一步开始
for j := 0; j < fn+1; j++ {
temp0.Data[j] = sol0.GetFromMatrix(j, i-1)
}
k1 := goNum.ZeroMatrix(fn, 1)
k2 := goNum.ZeroMatrix(fn, 1)
k3 := goNum.ZeroMatrix(fn, 1)
k4 := goNum.ZeroMatrix(fn, 1)
k5 := goNum.ZeroMatrix(fn, 1)
k6 := goNum.ZeroMatrix(fn, 1)
//1. k1
for j := 0; j < fn; j++ { //微分方程迭代
k1.Data[j] = h * fun(temp0, j)
}
//2. k2
temp0.Data[0] = sol0.GetFromMatrix(0, i-1) + h/4.0 //xn+h/4
for j := 1; j < fn+1; j++ { //yn+k1/4
temp0.Data[j] = sol0.GetFromMatrix(j, i-1) + k1.Data[j-1]/4.0
}
for j := 0; j < fn; j++ { //微分方程迭代
k2.Data[j] = h * fun(temp0, j)
}
//3. k3
temp0.Data[0] = sol0.GetFromMatrix(0, i-1) + 3.0*h/8.0 //xn+3h/8
for j := 1; j < fn+1; j++ { //yn+3k1/32+9k2/32
temp0.Data[j] = sol0.GetFromMatrix(j, i-1) + 3.0*k1.Data[j-1]/32.0 +
9.0*k2.Data[j-1]/32.0
}
for j := 0; j < fn; j++ { //微分方程迭代
k3.Data[j] = h * fun(temp0, j)
}
//4. k4
temp0.Data[0] = sol0.GetFromMatrix(0, i-1) + 12.0*h/13.0 //xn+12h/13
for j := 1; j < fn+1; j++ { //yn+1932k1/2197-7200k2/2197+7296k3/2197
temp0.Data[j] = sol0.GetFromMatrix(j, i-1) + 1932.0*k1.Data[j-1]/2197.0 -
7200.0*k2.Data[j-1]/2197.0 + 7296.0*k3.Data[j-1]/2197.0
}
for j := 0; j < fn; j++ { //微分方程迭代
k4.Data[j] = h * fun(temp0, j)
}
//5. k5
temp0.Data[0] = sol0.GetFromMatrix(0, i-1) + h //xn+h
for j := 1; j < fn+1; j++ { //yn+439k1/216-8k2+3680k3/513-845k4/4104
temp0.Data[j] = sol0.GetFromMatrix(j, i-1) + 439.0*k1.Data[j-1]/216.0 -
8.0*k2.Data[j-1] + 3680.0*k3.Data[j-1]/513.0 - 845.0*k4.Data[j-1]/4104.0
}
for j := 0; j < fn; j++ { //微分方程迭代
k5.Data[j] = h * fun(temp0, j)
}
//6. k6
temp0.Data[0] = sol0.GetFromMatrix(0, i-1) + h/2.0 //xn+h/2
for j := 1; j < fn+1; j++ { //yn-8k1/27+2k2-3544k3/2565+1859k4/4104-11k5/40
temp0.Data[j] = sol0.GetFromMatrix(j, i-1) - 8.0*k1.Data[j-1]/27.0 +
2.0*k2.Data[j-1] - 3544.0*k3.Data[j-1]/2565.0 + 1859.0*k4.Data[j-1]/4104.0 -
11.0*k5.Data[j-1]/40.0
}
for j := 0; j < fn; j++ { //微分方程迭代
k6.Data[j] = h * fun(temp0, j)
}
//误差与步长
errtemp := goNum.ZeroMatrix(fn, 1) //=ABS(z_(k+1)-y_(k+1))
for j := 1; j < fn+1; j++ {
errtemp.Data[j-1] = k1.Data[j-1]/360.0 - 128.0*k3.Data[j-1]/4275.0 -
2197.0*k4.Data[j-1]/75240.0 + k5.Data[j-1]/50.0 + 2.0*k6.Data[j-1]/55.0
}
errtemp0, _, _ := goNum.MaxAbs(errtemp.Data)
//正常推进
if math.Abs(errtemp0) < tol {
//i步值
sol0.SetMatrix(0, i, sol0.GetFromMatrix(0, i-1)+h) //xi
for j := 1; j < fn+1; j++ {
soltemp1 := 25.0*k1.Data[j-1]/216.0 + 1408.0*k3.Data[j-1]/2565.0 +
2197.0*k4.Data[j-1]/4104.0 - k5.Data[j-1]/5.0
soltemp1 = sol0.GetFromMatrix(j, i-1) + soltemp1
sol0.SetMatrix(j, i, soltemp1)
}
i++
nreal = i
continue
}
//最大步数强边界
if i >= n {
break
}
//变步长
scale := tol * h / (2.0 * math.Abs(errtemp0))
scale = math.Pow(scale, 0.25)
if scale < 0.75 {
h = h / 2.0
} else if scale > 1.5 {
h = h * 2.0
}
}
//解矩阵缩减
sol := goNum.ZeroMatrix(fn+1, nreal)
for j := 0; j < nreal; j++ {
for k := 0; k < fn+1; k++ {
sol.SetMatrix(k, j, sol0.GetFromMatrix(k, j))
}
}
err = true
return sol, err
}
func fun43(x0 goNum.Matrix, i int) float64 {
switch i {
case 0:
return 1.0 + x0.Data[1]*x0.Data[1]
default:
return 0.0
}
return 0.0
}
func BenchmarkRKF45(b *testing.B) {
x43 := goNum.NewMatrix(2, 1, []float64{0.0, 0.0})
for i := 0; i < b.N; i++ {
goNum.RKF45(fun43, x43, 1.4, 2e-5, 1, 700)
}
}