计算流体力学简介(五)——通量差分

基本原理

守恒型方程
ut+fx=0\frac{\partial u}{\partial t}+\frac{\partial f}{\partial x}=0
使用空间中心差分离散
uit+fi+12fi12Δx=0\frac{\partial u_i}{\partial t}+\frac{f_{i+\frac{1}{2}}-f_{i-\frac{1}{2}}}{\Delta x}=0
其中i+12i+\frac{1}{2}i12i-\frac{1}{2}表示网格点ii与两侧网格点i+1i+1i1i-1的中点。构造函数fi+12=g(uik,,ui,,ui+l)f_{i+\frac{1}{2}}=g(u_{i-k},\cdots,u_i,\cdots,u_{i+l})(fi12=f(i1)+12f_{i-\frac{1}{2}}=f_{(i-1)+\frac{1}{2}})使得g(ui+12,,ui+12)=f(ui+12)g(u_{i+\frac{1}{2}},\cdots,u_{i+\frac{1}{2}})=f(u_{i+\frac{1}{2}})则称函数gg构造出的fi+12f_{i+\frac{1}{2}}为数值通量。利用uit+fi+12fi12Δx=0\frac{\partial u_i}{\partial t}+\frac{f_{i+\frac{1}{2}}-f_{i-\frac{1}{2}}}{\Delta x}=0进行时间离散推进得到的格式为守恒型差分格式。而构造函数gg即是对通量ff的重构,因此这个方法称为通量重构。
这个方法保证了通量在网格单元的交界面(i+12i+\frac{1}{2})两侧始终是相等的,不会因为网格离散而产生数值散度,从而在网格交界面上出现非物理的源和汇。

对流方程

ut+ux=0\frac{\partial u}{\partial t}+\frac{\partial u}{\partial x}=0
初场为u0(x)=ex2x[5,5]u_0(x)=e^{-x^2},x\in[-5,5]周期边界
ui+12=ui+ui+12u_{i+\frac{1}{2}}=\frac{u_i+u_{i+1}}{2},时间推进使用欧拉单步推进,得到离散方程
uin+1uinΔt+ui+1n+uin2uin+ui1n2Δx=0\frac{u_i^{n+1}-u_i^n}{\Delta t}+\frac{\frac{u_{i+1}^n+u_i^n}{2}-\frac{u_i^n+u_{i-1}^n}{2}}{\Delta x}=0化简之后我们可一看到就是前面提到的一阶中心差分的格式,根据前面一阶中心差分格式的分析可以知道这个格式不稳定,需要添加人工粘性,这里就不解这个方程了。

Burgers方程

利用通量重构求解
ut+uux=0\frac{\partial u}{\partial t}+u\frac{\partial u}{\partial x}=0
初场为u0(x)=ex2x[5,5]u_0(x)=e^{-x^2},x\in[-5,5]周期边界
f(u)=u22f(u)=\frac{u^2}{2}
同样使用上面的离散方法进行离散得到
uin+1=uinΔt4Δx((ui+1n)2(ui1n)2)u_i^{n+1}=u_i^n-\frac{\Delta t}{4\Delta x}((u_{i+1}^n)^2-(u_{i-1}^n)^2)
具体代码如下

#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
const int NE=32,//空间点数
NS=100;//时间步数
const double rb=-5,l=10,//计算域左边界,计算域长度
dt=0.1,//时间步长
dx=l/NE;
void init_guass(vector<double> &u0)//设置高斯函数初场
{
    for(int i=0;i<u0.size();i++) 
	{
		u0[i]=exp(-pow((l*double(i)/(NE-1)+rb),2));
	}
}
void advance(vector<double>& u)
{
    vector<double> t(u);
    for(int i=1;i<u.size()-1;i++)
    {
        u[i]=t[i]-0.25*(t[i+1]*t[i+1]-t[i-1]*t[i-1])*dt/dx;
    }
    int i=0;
    u[i]=t[i]-0.25*(t[i+1]*t[i+1]-t[u.size()-1]*t[u.size()-1])*dt/dx;
    i=u.size()-1;
    u[i]=t[i]-0.25*(t[0]*t[0]-t[i-1]*t[i-1])*dt/dx;
}
ostream& operator<<(ostream& out,const vector<double>& A)
{
        for(int j=0;j<A.size()-1;j++)
        {
            out<<A[j]<<'\t';
        }
        out<<A[A.size()-1];
    return out;
}
int main()
{
    vector<double> u(NE+1);
    init_guass(u);
    cout<<NE+1<<'\t'<<NS<<'\t'<<rb<<'\t'<<l<<'\n';
    cout<<u<<'\n';
    for(int i=0;i<NS;i++)
    {
        advance(u);
        cout<<u<<'\n';
    }
    return 0;
}

在这里插入图片描述
可以看到计算发散了,从前面对流方程可以看到以这种单元两侧的平均值作为通量点的值实际上是一种中心差分格式,那么显示推进必然不稳定,必须添加人工粘性。因此这里考虑使用迎风格式来进行计算。
fi+12=ui2f_{i+\frac{1}{2}}=u_{i}^2则得到推进公式为
uin+1=uin+Δt2Δx((uin)2(ui1n)2)u_i^{n+1}=u_i^n+\frac{\Delta t}{2\Delta x}((u_i^n)^2-(u_{i-1}^n)^2)
得到advance函数为

void advance(vector<double>& u)
{
    vector<double> t(u);
    for(int i=1;i<u.size()-1;i++)
    {
        u[i]=t[i]-0.5*(t[i]*t[i]-t[i-1]*t[i-1])*dt/dx;
    }
    int i=0;
    u[i]=t[i]-0.5*(t[i]*t[i]-t[u.size()-1]*t[u.size()-1])*dt/dx;
}

计算结果如下
在这里插入图片描述
可以看到使用迎风的通量重构即可得到稳定的显式推进格式,并且结果也基本上没有明显问题。
这里使用更高阶格式尝试一下。
fi+12=fi+12fiΔx+O(Δx)fi=fifi1=fifiΔx+O(Δx)f_{i+\frac{1}{2}}=f_i+\frac{1}{2}f_i'\Delta x+O(\Delta x)\\ f_i=f_i\\ f_{i-1}=f_i-f_i'\Delta x+O(\Delta x)
可以得到
fi+12=3fifi12f_{i+\frac{1}{2}}=\frac{3f_i-f_{i-1}}{2}
于是有新的推进格式
uin+1=uinΔt4Δx(3(uin)24(ui1n)2+(ui2n)2)u_i^{n+1}=u_i^n-\frac{\Delta t}{4\Delta x}(3(u_i^n)^2-4(u_{i-1}^n)^2+(u_{i-2}^n)^2)
得到新的advance函数为

void advance(vector<double>& u)
{
    vector<double> t(u);
    for(int i=1;i<u.size()-1;i++)
    {
        u[i]=t[i]-0.25*(3*t[i]*t[i]-4*t[i-1]*t[i-1]+t[i-2]*t[i-2])*dt/dx;
    }
    int i=0;
    u[i]=t[i]-0.25*(3*t[i]*t[i]-4*t[u.size()-1]*t[u.size()-1]+t[u.size()-2]*t[u.size()-2])*dt/dx;
    i=1;
    u[i]=t[i]-0.25*(3*t[i]*t[i]-4*t[i-1]*t[i-1]+t[u.size()-1]*t[u.size()-1])*dt/dx;
}

计算结果如下
在这里插入图片描述
可以看到间断部分更加尖锐,更贴近真解,不像之前那样间断的拐角处比较光滑。但是可以看到在间断结束的位置有一个向下的小尖峰,这是使用高阶耗散(高于二阶的耗散)时总会出现的一个现象,具体产生的原因我也不是很清楚,似乎类似于吉布斯现象。

发布了23 篇原创文章 · 获赞 9 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章