計算流體力學簡介(八)——激波管問題

問題描述

求解如下方程
u=[ρρuE],F=[ρuρu2+pu(E+p)],E=pγ1+12ρu2 ut+Fx=0u=\left[\begin{matrix} \rho \\ \rho u\\ E \end{matrix}\right], F=\left[\begin{matrix} \rho u\\ \rho u^2+p\\ u(E+p) \end{matrix}\right], E=\frac{p}{\gamma-1}+\frac{1}{2}\rho u^2\\ \ \\ \frac{\partial u}{\partial t}+\frac{\partial F}{\partial x}=0
的間斷初值問題稱爲激波管問題也叫黎曼問題。
該問題描述的是無粘理想氣體在中一根管道中由壓力或者速度驅動在突變的初始條件下的變化過程。
通過求解通量的雅各比矩陣A=FuA=\frac{\partial {\vec F}}{\partial {\vec u}}可以將前面的控制方程寫成
ut+Aux=0\frac{\partial u}{\partial t}+A\frac{\partial u}{\partial x}=0
其中
u=[ρρuE],A=[0103γ2u2(3γ)uγ1u(1γ1c2+2γ2u2)1γ1c2+32γ2u2γu]u=\left[\begin{matrix} \rho \\ \rho u\\ E \end{matrix}\right], A=\left[\begin{matrix} 0&1&0 \\ -\frac{3-\gamma}{2}u^2&(3-\gamma)u&\gamma-1\\ -u(\frac{1}{\gamma -1}c^2+\frac{2-\gamma}{2}u^2)&\frac{1}{\gamma -1}c^2+\frac{3-2\gamma}{2}u^2&\gamma u \end{matrix}\right]
其中c=γpρc=\sqrt{\gamma\frac{p}{\rho}}是聲速
對於該方程存在一個特殊關係
以守恆變量表示的守恆形式和非守恆形式的方程之間滿足雅各比矩陣AA有齊次性
dF=AdudF=AduF=AuF=Au

任何與AA相似的矩陣BB以及相應的可逆變換PP滿足A=P1BPA=P^{-1}BP,都可以得到一個新的等價形式
(Pu)t+B(Pu)x=0\frac{\partial (Pu)}{\partial t}+B\frac{\partial (Pu)}{\partial x}=0
於是根據原變量ρ,u,p\rho,u,p可以得到相應的等價形式
ut+Aux=0u=[ρup],A=[uρ00u1ρ0ρc2u]\frac{\partial u}{\partial t}+A\frac{\partial u}{\partial x}=0\\ u=\left[\begin{matrix} \rho \\ u\\ p \end{matrix}\right], A=\left[\begin{matrix} u&\rho&0 \\ 0&u&\frac{1}{\rho}\\ 0&\rho c^2&u \end{matrix}\right]
通過原變量形式的方程可以很容易計算出矩陣AA的特徵值,從而得到與AA相似的對角陣,將方程解耦,得到三個方程。分別求出三個方程的特徵線並對應三個變量,沿三條特徵線三個變量的值不發生改變,這三個變量稱爲黎曼不變量。

問題求解

在這裏插入圖片描述
一根兩端封閉的管道內左右兩側有不同的氣體,中間以一物體將兩側氣體分隔開,左側氣體滿足ρL=1,uL=0,pL=1\rho_L=1,u_L=0,p_L=1右側氣體滿足ρL=2,uL=0,pL=2\rho_L=2,u_L=0,p_L=2現在突然撤去中間分隔物,兩側氣體將在壓力作用下相互混合,最終達到平衡狀態。
這裏以二階迎風的通量差分格式計算。
由原變量表達的方程中空間導數項的係數矩陣AA的特徵值爲u+c,uc,uu+c,u-c,u分別表示三個黎曼不變量的傳播速度,其中本問題中顯然一定有亞音速的區域,因此必然有u+c>0,uc<0u+c>0,u-c<0說明方程本身既有向左傳播的信息又有向右傳播的信息。這時就需要根據信息傳播方向決定使用哪一側的迎風格式。
通量分裂的方法有很多,這裏使用Lax-Friedrichs分裂
前面提到了雅各比矩陣的特徵值是u,u+c,ucu,u+c,u-c那麼根據當地的ρ,u,p\rho,u,p可以很容易計算出三個特徵值的值,找到三者中模最大的特徵值λmax|\lambda|_{max}然後構造新的雅各比矩陣A+=A+λmaxI2,A=AλmaxI2A^+=\frac{A+|\lambda|_{max}I}{2},A^-=\frac{A-|\lambda|_{max}I}{2}這樣必然有A=A++AA=A^++A^-A+A^+的全部特徵值大於0,AA^-的全部特徵值小於0,利用守恆型變量雅各比矩陣的齊次性有
F+=A+u=F+λmaxu2,F=Au=Fλmaxu2F^+=A^+u=\frac{F+|\lambda|_{max}u}{2},F^-=A^-u=\frac{F-|\lambda|_{max}u}{2}
於是得到分裂的守恆方程
ut+F+x+Fx=0\frac{\partial u}{\partial t}+\frac{\partial F^+}{\partial x}+\frac{\partial F^-}{\partial x}=0
式中第二項表示向右傳播的信息,第三項表示向左傳播的信息,分別使用3I4E1+E22Δx\frac{3I-4E^{-1}+E^{-2}}{2\Delta x}E24E1+3I2Δx-\frac{E^2-4E^1+3I}{2\Delta x}進行離散。
時間使用單步推進。
Δt=0.005,Δx=0.1\Delta t=0.005,\Delta x=0.1
具體代碼如下

#include <iostream>
#include <vector>
#include <cmath>
#define gamma 1.4
const int NE=100,//空間點數
NS=1000,
SKIP_STEP=10;//時間步數
const double rb=-5,l=10,//計算域左邊界,計算域長度
dt=0.005,//時間步長
dx=l/NE;
using namespace std;
void F(vector<double> &_F,double w1,double w2,double w3)
{
    double u=w2/w1,t=u*w2;
    _F[0]=w2;
    _F[1]=(3-gamma)*t/2+(gamma-1)*w3;
    _F[2]=(1-gamma)/2*u*t+gamma*u*w3;
}
void F_div(vector<double>::iterator &f,const vector<double> &F,double w1,double w2,double w3)
{
    *f=F[0];
    f++;
    *f=F[1];
    f++;
    *f=F[2];
    f++;
}
double max(double x1,double x2)
{
    if(x1>x2) return x1;
    else return x2;
}
void advance(vector<double>& w1,vector<double>& w2,vector<double>& w3,vector<double>& F_p,vector<double>& F_m)
{
    vector<double> tF(3,0);
    double l=0;
    vector<double>::iterator f_p=F_p.begin(),f_m=F_m.begin();
    for(int i=0;i<w1.size();i++)
    {
        F(tF,w1[i],w2[i],w3[i]);
        double u=w2[i]/w1[i],p=(gamma-1)*(w3[i]-w2[i]*w2[i]/w1[i]/2),c=sqrt(gamma*p/w1[i]);
        l=max(max(abs(u+c),abs(u-c)),l);
        F_div(f_p,tF,w1[i],w2[i],w3[i]);
        F_div(f_m,tF,w1[i],w2[i],w3[i]);
    }
    for(int i=0;i<w1.size();i++)
    {
        F_p[3*i]+=l*w1[i];
        F_m[3*i]-=l*w1[i]; 
        F_p[3*i+1]+=l*w2[i];
        F_m[3*i+1]-=l*w2[i];  
        F_p[3*i+2]+=l*w3[i];
        F_m[3*i+2]-=l*w3[i]; 
    }
    f_p=F_p.begin()+3,f_m=F_m.begin()+3;
    w1[1]=w1[1]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w2[1]=w2[1]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w3[1]=w3[1]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    for(int i=2;i<w1.size()-2;i++)
    {
        
        w1[i]=w1[i]-0.25*(3*(*f_p)-4*(*(f_p-3))+*(f_p-6))*dt/dx
                   +0.25*(*(f_m+6)-4*(*(f_m+3))+3*(*(f_m)))*dt/dx;
        f_p++;
        f_m++;
        w2[i]=w2[i]-0.25*(3*(*f_p)-4*(*(f_p-3))+*(f_p-6))*dt/dx
                   +0.25*(*(f_m+6)-4*(*(f_m+3))+3*(*(f_m)))*dt/dx;
        f_p++;
        f_m++;
        w3[i]=w3[i]-0.25*(3*(*f_p)-4*(*(f_p-3))+*(f_p-6))*dt/dx
                   +0.25*(*(f_m+6)-4*(*(f_m+3))+3*(*(f_m)))*dt/dx;
        f_p++;
        f_m++;
    }
    w1[w1.size()-2]=w1[w1.size()-2]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w2[w2.size()-2]=w2[w2.size()-2]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w3[w3.size()-2]=w3[w3.size()-2]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w1[0]=w1[1];
    w2[0]=-w2[1];
    w3[0]=w3[1];
    w1[w1.size()-1]=w1[w1.size()-2];
    w2[w2.size()-1]=-w2[w2.size()-2];
    w3[w3.size()-1]=w3[w3.size()-2];

}
struct val
{
    const vector<double> &w1,&w2,&w3;
    val(const vector<double>& _w1,const vector<double>& _w2,const vector<double>& _w3):w1(_w1),w2(_w2),w3(_w3){};
};
void init(vector<double> &w1,vector<double> &w2,vector<double> &w3)
{
    int i=1;
    for(;i<w1.size()/2;i++)
    {
        w1[i]=1;
        w2[i]=0;
        w3[i]=2/(gamma-1);
    }
    for(;i<w1.size()-1;i++)
    {
        w1[i]=1;
        w2[i]=0;
        w3[i]=1/(gamma-1);
    }
    w1[0]=w1[1];
    w2[0]=-w2[1];
    w3[0]=w3[1];
    w1[w1.size()-1]=w1[w1.size()-2];
    w2[w2.size()-1]=-w2[w2.size()-2];
    w3[w3.size()-1]=w3[w3.size()-2];
}
ostream& operator<<(ostream& out,const val& Q)
{
    for(int i=1;i<Q.w1.size()-1;i++)
    {
        double rho=Q.w1[i],u=Q.w2[i]/Q.w1[i],p=(gamma-1)*(Q.w3[i]-Q.w2[i]*Q.w2[i]/Q.w1[i]/2);
        out<<i*dx+rb-dx<<'\t'<<rho<<'\t'<<u<<'\t'<<p<<'\n';
    }
    return out;
}
int main()
{
    vector<double> w1(NE+3),w2(NE+3),w3(NE+3),F_p(3*NE+9),F_m(3*NE+9);
    val Q(w1,w2,w3);
    init(w1,w2,w3);
    cout<<w1.size()-2<<'\t'<<NS/SKIP_STEP<<'\t'<<rb<<'\t'<<l<<'\n';
    cout<<Q<<'\n';
    for(int i=0;i<NS;i++)
    {
        advance(w1,w2,w3,F_p,F_m);
        if(i%SKIP_STEP==0)cout<<Q<<'\n';
    }
}

計算結果如下
藍線是壓力,黑線是密度,紅線是速度
在這裏插入圖片描述
由於我也沒有激波管解析解的結果。所以也沒辦法判斷具體結果差多少,不過從網上看到的一些激波管問題的解來看基本規律應該沒有太大的問題,不過由於二階迎風格式的耗散較小,因此間斷處有一些震盪。
要想消除震盪可以利用限制器在間斷附近使用耗散更大的低階通量或者使用ENO和WENO格式來抑制震盪。

現在先測試一下一階格式,使用如下advance函數

void advance(vector<double>& w1,vector<double>& w2,vector<double>& w3,vector<double>& F_p,vector<double>& F_m)
{
    vector<double> tF(3,0);
    double l=0;
    vector<double>::iterator f_p=F_p.begin(),f_m=F_m.begin();
    for(int i=0;i<w1.size();i++)
    {
        F(tF,w1[i],w2[i],w3[i]);
        double u=w2[i]/w1[i],p=(gamma-1)*(w3[i]-w2[i]*w2[i]/w1[i]/2),c=sqrt(gamma*p/w1[i]);
        l=max(max(abs(u+c),abs(u-c)),l);
        F_div(f_p,tF,w1[i],w2[i],w3[i]);
        F_div(f_m,tF,w1[i],w2[i],w3[i]);
    }
    for(int i=0;i<w1.size();i++)
    {
        F_p[3*i]+=l*w1[i];
        F_m[3*i]-=l*w1[i]; 
        F_p[3*i+1]+=l*w2[i];
        F_m[3*i+1]-=l*w2[i];  
        F_p[3*i+2]+=l*w3[i];
        F_m[3*i+2]-=l*w3[i]; 
    }
    f_p=F_p.begin()+3,f_m=F_m.begin()+3;
    w1[1]=w1[1]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w2[1]=w2[1]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w3[1]=w3[1]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    for(int i=2;i<w1.size()-2;i++)
    {
        w1[i]=w1[i]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
        f_p++;
        f_m++;
        w2[i]=w2[i]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
        f_p++;
        f_m++;
        w3[i]=w3[i]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
        f_p++;
        f_m++;
        
    }
    w1[w1.size()-2]=w1[w1.size()-2]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w2[w2.size()-2]=w2[w2.size()-2]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w3[w3.size()-2]=w3[w3.size()-2]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w1[0]=w1[1];
    w2[0]=-w2[1];
    w3[0]=w3[1];
    w1[w1.size()-1]=w1[w1.size()-2];
    w2[w2.size()-1]=-w2[w2.size()-2];
    w3[w3.size()-1]=w3[w3.size()-2];
}

結果如下
在這裏插入圖片描述
可以看到使用高耗散的低階格式間斷處的震盪就可以被抑制住。但是耗散非常明顯。

這裏使用限制器來抑制間斷震盪,首先相鄰確定差分的比值根據信息的傳播方向分成兩個方向的比值
ri+12=ui+2ui+1ui+1uiri+12+=uiui1ui1ui2r^-_{i+\frac{1}{2}}=\frac{u_{i+2}-u_{i+1}}{u_{i+1}-u_{i}}\\ r^+_{i+\frac{1}{2}}=\frac{u_{i}-u_{i-1}}{u_{i-1}-u_{i-2}}
限制器函數φ(r)=min(1,r)\varphi(r)=min(1,|r|)
格式如下
uin+1=uinΔt2Δx(φ(ri+12+)Fi+12+(1)φ(ri12+)Fi12+(1)+(1φ(ri+12))Fi+12(2)(1φ(ri+12))Fi+12(2))u_i^{n+1}=u_i^n-\frac{\Delta t}{2\Delta x} (\varphi(r^+_{i+\frac{1}{2}})F^{+(1)}_{i+\frac{1}{2}}-\varphi(r^+_{i-\frac{1}{2}})F^{+(1)}_{i-\frac{1}{2}}+ (1-\varphi(r^-_{i+\frac{1}{2}}))F^{-(2)}_{i+\frac{1}{2}}-(1-\varphi(r^-_{i+\frac{1}{2}}))F^{-(2)}_{i+\frac{1}{2}})
這裏做了一個簡化,直接令
ri+=uiui1ui1ui2ri=ui+2ui+1ui+1uir^+_i=\frac{u_{i}-u_{i-1}}{u_{i-1}-u_{i-2}}\\ r^-_i=\frac{u_{i+2}-u_{i+1}}{u_{i+1}-u_{i}}
φ(ri)\varphi(r_i)來決定使用一階還是二階格式

ut+φ(r+)F+(2)x+φ(r)F(2)x+(1φ(r+))F+(1)x+(1φ(r))F(1)x=0\frac{\partial u}{\partial t}+\varphi(r^+)\frac{\partial F^{+(2)}}{\partial x}+\varphi(r^-)\frac{\partial F^{-(2)}}{\partial x}+(1-\varphi(r^+))\frac{\partial F^{+(1)}}{\partial x}+(1-\varphi(r^-))\frac{\partial F^{-(1)}}{\partial x}=0
具體的advance函數如下

void advance(vector<double>& w1,vector<double>& w2,vector<double>& w3,vector<double>& F_p,vector<double>& F_m)
{
    vector<double> tF(3,0);
    double l=0;
    vector<double>::iterator f_p=F_p.begin(),f_m=F_m.begin();
    for(int i=0;i<w1.size();i++)
    {
        F(tF,w1[i],w2[i],w3[i]);
        double u=w2[i]/w1[i],p=(gamma-1)*(w3[i]-w2[i]*w2[i]/w1[i]/2),c=sqrt(gamma*p/w1[i]);
        l=max(max(abs(u+c),abs(u-c)),l);
        F_div(f_p,tF,w1[i],w2[i],w3[i]);
        F_div(f_m,tF,w1[i],w2[i],w3[i]);
    }
    for(int i=0;i<w1.size();i++)
    {
        F_p[3*i]+=l*w1[i];
        F_m[3*i]-=l*w1[i]; 
        F_p[3*i+1]+=l*w2[i];
        F_m[3*i+1]-=l*w2[i];  
        F_p[3*i+2]+=l*w3[i];
        F_m[3*i+2]-=l*w3[i]; 
    }
    f_p=F_p.begin()+3,f_m=F_m.begin()+3;
    w1[1]=w1[1]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w2[1]=w2[1]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w3[1]=w3[1]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    for(int i=2;i<w1.size()-2;i++)
    {
        double r_p=(w1[i]-w1[i-1])/(w1[i-1]-w1[i-2]),
               r_m=(w1[i+2]-w1[i+1])/(w1[i+1]-w1[i]);
               if(w1[i-1]==w1[i-2]) r_p=0;
               if(w1[i+1]==w1[i]) r_m=0;
        w1[i]=w1[i]-phi(r_p)*0.25*(3*(*f_p)-4*(*(f_p-3))+*(f_p-6))*dt/dx
                   -(1-phi(r_p))*0.5*(*(f_p)-*(f_p-3))*dt/dx
                   +phi(r_m)*0.25*(*(f_m+6)-4*(*(f_m+3))+3*(*(f_m)))*dt/dx
                   -(1-phi(r_m))*0.5*(*(f_m+3)-*(f_m))*dt/dx;
        f_p++;
        f_m++;
        r_p=(w2[i]-w2[i-1])/(w2[i-1]-w2[i-2]),
        r_m=(w2[i+2]-w2[i+1])/(w2[i+1]-w2[i]);
        if(w2[i-1]==w2[i-2]) r_p=0;
        if(w2[i+1]==w2[i]) r_m=0;
        w2[i]=w2[i]-phi(r_p)*0.25*(3*(*f_p)-4*(*(f_p-3))+*(f_p-6))*dt/dx
                   -(1-phi(r_p))*0.5*(*(f_p)-*(f_p-3))*dt/dx
                   +phi(r_m)*0.25*(*(f_m+6)-4*(*(f_m+3))+3*(*(f_m)))*dt/dx
                   -(1-phi(r_m))*0.5*(*(f_m+3)-*(f_m))*dt/dx;
        f_p++;
        f_m++;
        r_p=(w3[i]-w3[i-1])/(w3[i-1]-w3[i-2]),
        r_m=(w3[i+2]-w3[i+1])/(w3[i+1]-w3[i]);
        if(w3[i-1]==w3[i-2]) r_p=0;
        if(w3[i+1]==w3[i]) r_m=0;
        w3[i]=w3[i]-phi(r_p)*0.25*(3*(*f_p)-4*(*(f_p-3))+*(f_p-6))*dt/dx
                   -(1-phi(r_p))*0.5*(*(f_p)-*(f_p-3))*dt/dx
                   +phi(r_m)*0.25*(*(f_m+6)-4*(*(f_m+3))+3*(*(f_m)))*dt/dx
                   -(1-phi(r_m))*0.5*(*(f_m+3)-*(f_m))*dt/dx;
        f_p++;
        f_m++;
    }
    w1[w1.size()-2]=w1[w1.size()-2]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w2[w2.size()-2]=w2[w2.size()-2]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w3[w3.size()-2]=w3[w3.size()-2]-0.5*(*(f_p)-*(f_p-3))*dt/dx-0.5*(*(f_m+3)-*(f_m))*dt/dx;
    f_p++;
    f_m++;
    w1[0]=w1[1];
    w2[0]=-w2[1];
    w3[0]=w3[1];
    w1[w1.size()-1]=w1[w1.size()-2];
    w2[w2.size()-1]=-w2[w2.size()-2];
    w3[w3.size()-1]=w3[w3.size()-2];
}

Δt=0.001,Δx=0.1\Delta t=0.001,\Delta x=0.1
計算結果如下
在這裏插入圖片描述
對比前面一階格式和二階格式可以看到,使用限制器之後二階格式的間斷震盪被明顯抑制住了,而耗散也沒有一階格式那麼明顯。

發佈了23 篇原創文章 · 獲贊 9 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章