我使用预测校正器方法编写了两个用于解决差分问题的代码。
第一个也是用于求解微分系统,所以基本上它使用的矢量y
的大小等于在系统中求解的方程的数量,所以这一步基本上被覆盖每一步并且不存储之前的步骤。
第二个使用大小等于步数的向量,因此很容易控制我们正在使用的元素。
我写了两个不同的(但基本相同的)函数:一个用于第一个例程,第二个用于第二个例程
我做了很多调试,但我不明白我做错了什么。
以下是求解器的两个不同版本:
# include <iostream>
# include <functional>
# include <vector>
# include <string>
# include <cmath>
# include <fstream>
# include <valarray>
using namespace std;
std::vector<std::function<const double(const double t , std::valarray<double> u)>> func ;
auto f2 = [](double t , double u) {return -20*u+20*sin(t)+cos(t) ; } ;
auto numFun = [](double t , std::valarray<double> u) {return -20*u[0]+20*sin(t)+cos(t) ; } ;
int main (){
double t0 = 0.0 ;
double tf = 2.5 ;
double dt = 0.01;
const int N = (tf-t0)/dt;
func.push_back(numFun);
auto& f = func ;
double t1 ;
std::valarray<double> y1 ;
y1.resize(func.size()) ; // size = 1
std::valarray<double> yp1 ;
yp1.resize(func.size()) ; // size = 1
std::vector<double> t2(N+1) ;
std::vector<double> y2(N+1) ;
std::vector<double> yp2(N+1) ;
std::vector<double> u(1);
u.at(0) = 1.0 ;
t1 = t0 ;
for(auto i=0; i< u.size() ; i++)
{
y1[i]= u.at(i);
yp1[i] = u.at(i);
}
ofstream fn("output1.dat", ios::out);
for(auto i=1 ; i <=N ; i++ )
{
fn << t1 << " " ;
for(auto j =0 ; j < y1.size() ; j++ )
fn << y1[j] << " " ;
fn << endl ;
for(auto j=0 ; j < y1.size() ; j++)
{
yp1[j] = yp1[j] + dt * f[j](t1 , y1) ;
y1[j] += dt/2 * ( f[j](t1 , y1) + f[j]( t1+dt , yp1 ));
}
t1+=dt ;
}
fn.close();
/* -------------------- Vector version --------------------------------- */
ofstream fn2("output2.dat", ios::out);
t2.at(0) = t0 ;
y2.at(0) = u.at(0) ;
yp2.at(0) = u.at(0) ;
fn2 << t2.at(0) << " " ;
fn2 << y2.at(0) << " " ;
fn2 << endl ;
for(auto i=1; i <= N ; i++ )
{
t2.at(i) = t2.at(i-1) + dt ;
// Predictor step (Exp Euler)
yp2.at(i) = y2.at(i-1) + dt * f2(t2.at(i-1), y2.at(i-1)) ;
y2.at(i) = y2.at(i-1) + dt/2 * ( f2(t2.at(i-1), y2.at(i-1)) + f2(t2.at(i), yp2.at(i)) ) ;
fn2 << t2.at(i) << ' ' << y2.at(i) << " " ;
fn2 << endl;
}
fn2.close();
return 0;
}
尝试以这种方式使用3矢量!
for(t=t0() ; t < tf() ; t+= dt() )
{
f << t << ' ' ;
for(auto i=0 ; i< u.size() ; i++)
f << u[i] << " " ;
f << std::endl ;
for( auto i=0 ; i < u.size() ; i++)
{
up[i] = uc[i] + dt() * rhs.f[i](t , u) ;
uc[i] += dt()/2 * ( rhs.f[i](t , u) + rhs.f[i]( t +dt() , up ) );
u[i] = uc[i] ;
}
}
所以基本上预测变量必须是uc[i] + ...
,并且不能像标准欧拉公式中那样+=
(在这种情况下,他写了yp1[j]
= yp1[j] + ...
)而不是yp1[j] = y[j] + ...
对于第一种处理向量值ODE的方法,您需要将这三个步骤分成三个循环,对应于第二个方法中的向量运算。请注意,第二种方法在y1
的更新中包含一个隐式中间向量,因为在将其复制到y1
之前,首先使用y1
的旧值完全计算右侧。
for(auto i=1 ; i <=N ; i++ )
{
fn << t1 << " " ;
for(auto j =0 ; j < y1.size() ; j++ )
fn << y1[j] << " " ;
fn << (exp(-20*t1) + sin(t1)) << " ";
fn << endl ;
for(auto j=0 ; j < y1.size() ; j++)
{
yp1[j] = y1[j] + dt * f[j](t1 , y1) ;
}
for(auto j=0 ; j < y1.size() ; j++)
{
yc1[j] = y1[j] + dt/2 * ( f[j](t1 , y1) + f[j]( t1+dt , yp1 ));
}
for(auto j=0 ; j < y1.size() ; j++)
{
y1[j] = yc1[j];
}
t1+=dt ;
}