基本原理
守恒型方程
∂
u
∂
t
+
∂
f
∂
x
=
0
\frac{\partial u}{\partial t}+\frac{\partial f}{\partial x}=0
∂t∂u+∂x∂f=0
使用空间中心差分离散
∂
u
i
∂
t
+
f
i
+
1
2
−
f
i
−
1
2
Δ
x
=
0
\frac{\partial u_i}{\partial t}+\frac{f_{i+\frac{1}{2}}-f_{i-\frac{1}{2}}}{\Delta x}=0
∂t∂ui+Δxfi+21−fi−21=0
其中
i
+
1
2
i+\frac{1}{2}
i+21和
i
−
1
2
i-\frac{1}{2}
i−21表示网格点
i
i
i与两侧网格点
i
+
1
i+1
i+1和
i
−
1
i-1
i−1的中点。构造函数
f
i
+
1
2
=
g
(
u
i
−
k
,
⋯
,
u
i
,
⋯
,
u
i
+
l
)
f_{i+\frac{1}{2}}=g(u_{i-k},\cdots,u_i,\cdots,u_{i+l})
fi+21=g(ui−k,⋯,ui,⋯,ui+l)(
f
i
−
1
2
=
f
(
i
−
1
)
+
1
2
f_{i-\frac{1}{2}}=f_{(i-1)+\frac{1}{2}}
fi−21=f(i−1)+21)使得
g
(
u
i
+
1
2
,
⋯
,
u
i
+
1
2
)
=
f
(
u
i
+
1
2
)
g(u_{i+\frac{1}{2}},\cdots,u_{i+\frac{1}{2}})=f(u_{i+\frac{1}{2}})
g(ui+21,⋯,ui+21)=f(ui+21)则称函数
g
g
g构造出的
f
i
+
1
2
f_{i+\frac{1}{2}}
fi+21为数值通量。利用
∂
u
i
∂
t
+
f
i
+
1
2
−
f
i
−
1
2
Δ
x
=
0
\frac{\partial u_i}{\partial t}+\frac{f_{i+\frac{1}{2}}-f_{i-\frac{1}{2}}}{\Delta x}=0
∂t∂ui+Δxfi+21−fi−21=0进行时间离散推进得到的格式为守恒型差分格式。而构造函数
g
g
g即是对通量
f
f
f的重构,因此这个方法称为通量重构。
这个方法保证了通量在网格单元的交界面(
i
+
1
2
i+\frac{1}{2}
i+21)两侧始终是相等的,不会因为网格离散而产生数值散度,从而在网格交界面上出现非物理的源和汇。
对流方程
∂
u
∂
t
+
∂
u
∂
x
=
0
\frac{\partial u}{\partial t}+\frac{\partial u}{\partial x}=0
∂t∂u+∂x∂u=0
初场为
u
0
(
x
)
=
e
−
x
2
,
x
∈
[
−
5
,
5
]
u_0(x)=e^{-x^2},x\in[-5,5]
u0(x)=e−x2,x∈[−5,5]周期边界
令
u
i
+
1
2
=
u
i
+
u
i
+
1
2
u_{i+\frac{1}{2}}=\frac{u_i+u_{i+1}}{2}
ui+21=2ui+ui+1,时间推进使用欧拉单步推进,得到离散方程
u
i
n
+
1
−
u
i
n
Δ
t
+
u
i
+
1
n
+
u
i
n
2
−
u
i
n
+
u
i
−
1
n
2
Δ
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
Δtuin+1−uin+Δx2ui+1n+uin−2uin+ui−1n=0化简之后我们可一看到就是前面提到的一阶中心差分的格式,根据前面一阶中心差分格式的分析可以知道这个格式不稳定,需要添加人工粘性,这里就不解这个方程了。
Burgers方程
利用通量重构求解
∂
u
∂
t
+
u
∂
u
∂
x
=
0
\frac{\partial u}{\partial t}+u\frac{\partial u}{\partial x}=0
∂t∂u+u∂x∂u=0
初场为
u
0
(
x
)
=
e
−
x
2
,
x
∈
[
−
5
,
5
]
u_0(x)=e^{-x^2},x\in[-5,5]
u0(x)=e−x2,x∈[−5,5]周期边界
f
(
u
)
=
u
2
2
f(u)=\frac{u^2}{2}
f(u)=2u2
同样使用上面的离散方法进行离散得到
u
i
n
+
1
=
u
i
n
−
Δ
t
4
Δ
x
(
(
u
i
+
1
n
)
2
−
(
u
i
−
1
n
)
2
)
u_i^{n+1}=u_i^n-\frac{\Delta t}{4\Delta x}((u_{i+1}^n)^2-(u_{i-1}^n)^2)
uin+1=uin−4ΔxΔt((ui+1n)2−(ui−1n)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;
}
可以看到计算发散了,从前面对流方程可以看到以这种单元两侧的平均值作为通量点的值实际上是一种中心差分格式,那么显示推进必然不稳定,必须添加人工粘性。因此这里考虑使用迎风格式来进行计算。
令
f
i
+
1
2
=
u
i
2
f_{i+\frac{1}{2}}=u_{i}^2
fi+21=ui2则得到推进公式为
u
i
n
+
1
=
u
i
n
+
Δ
t
2
Δ
x
(
(
u
i
n
)
2
−
(
u
i
−
1
n
)
2
)
u_i^{n+1}=u_i^n+\frac{\Delta t}{2\Delta x}((u_i^n)^2-(u_{i-1}^n)^2)
uin+1=uin+2ΔxΔt((uin)2−(ui−1n)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;
}
计算结果如下
可以看到使用迎风的通量重构即可得到稳定的显式推进格式,并且结果也基本上没有明显问题。
这里使用更高阶格式尝试一下。
f
i
+
1
2
=
f
i
+
1
2
f
i
′
Δ
x
+
O
(
Δ
x
)
f
i
=
f
i
f
i
−
1
=
f
i
−
f
i
′
Δ
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+21=fi+21fi′Δx+O(Δx)fi=fifi−1=fi−fi′Δx+O(Δx)
可以得到
f
i
+
1
2
=
3
f
i
−
f
i
−
1
2
f_{i+\frac{1}{2}}=\frac{3f_i-f_{i-1}}{2}
fi+21=23fi−fi−1
于是有新的推进格式
u
i
n
+
1
=
u
i
n
−
Δ
t
4
Δ
x
(
3
(
u
i
n
)
2
−
4
(
u
i
−
1
n
)
2
+
(
u
i
−
2
n
)
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)
uin+1=uin−4ΔxΔt(3(uin)2−4(ui−1n)2+(ui−2n)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;
}
计算结果如下
可以看到间断部分更加尖锐,更贴近真解,不像之前那样间断的拐角处比较光滑。但是可以看到在间断结束的位置有一个向下的小尖峰,这是使用高阶耗散(高于二阶的耗散)时总会出现的一个现象,具体产生的原因我也不是很清楚,似乎类似于吉布斯现象。