有限体积法(13)——SIMPLE算法的一维算例

算例模型

本文将使用SIMPLE算法来计算一个简单算例,以展示SIMPLE算法的具体事实过程。计算模型是一个平面喷嘴结构,尺寸数据如下图,假设流动是稳态无粘不可压的,计算速度和压力分布。有限体积法(13)——SIMPLE算法的一维算例_第1张图片
参数:
流体密度: 1.0 k g / m 3 1.0 kg/m^3 1.0kg/m3
喷嘴长度: 2 m 2m 2m
截面面积:入口: 0.5 m 2 0.5m^2 0.5m2,出口: 0.1 m 2 0.1m^2 0.1m2,截面面积与距入口距离是线性关系
边界条件:
入口给定滞止压力 10 P a 10Pa 10Pa,出口给定静压 0 P a 0 Pa 0Pa
精确解:
本算例可以使用伯努利方程 p 0 = p N + 1 2 ρ u N 2 p_0=p_N+\frac{1}{2}\rho u^2_N p0=pN+21ρuN2作为解析解,速度使用截面平均速度。

网格划分

采用后向交错网格,如下图,压力节点5个,速度节点4个。
有限体积法(13)——SIMPLE算法的一维算例_第2张图片

初始流场和压力场

假设质量流量 m ˙ = 1.0 k g / s \dot m = 1.0 kg/s m˙=1.0kg/s,各截面上的速度用 u = m ˙ / ( ρ A ) u= \dot m /(\rho A) u=m˙/(ρA)来计算。压力场假设是线性分布的,根据入口和出口的边界条件,初始场数据见下表:

节点 截面面积 ( m 2 ) (m^2) (m2) 初始压力 ( P a ) (Pa) (Pa) 初始速度 ( m / s ) (m/s) (m/s)
A 0.5 10
1 0.45 2.222
B 0.4 7.5
2 0.35 2.857
C 0.3 5
3 0.25 4.000
D 0.2 2.5
4 0.15 6.667
E 0.1 0

计算过程分析

控制方程进行一维、稳态、无粘、不可压的简化后如下:
质量守恒:
d d x ( ρ A u ) = 0 (1-a) \frac{d}{dx}(\rho A u)=0 \tag{1-a} dxd(ρAu)=0(1-a)

动量守恒:
ρ u A d u d x = − A d p d x (1-b) \rho u A \frac{du}{dx}=-A\frac{dp}{dx} \tag{1-b} ρuAdxdu=Adxdp(1-b)

离散动量方程

对动量方程 ( 1 − b ) (1-b) (1b)进行离散,得
( ρ u A ) e u e − ( ρ u A ) w u w = Δ p Δ x Δ V (2) \begin{aligned} (\rho u A)_e u_e - (\rho u A)_w u_w = \frac{\Delta p}{\Delta x} \Delta V \end{aligned} \tag{2} (ρuA)eue(ρuA)wuw=ΔxΔpΔV(2)
其中 Δ p = p w − p e \Delta p=p_w - p_e Δp=pwpe Δ V \Delta V ΔV是控制单元的体积。

离散动量方程可写成如下标准形式
a P u P ∗ = a W u W ∗ + a E u E ∗ + S u (3) \begin{aligned} a_P u^*_P = a_W u^*_W + a_E u^*_E + S_u \end{aligned} \tag{3} aPuP=aWuW+aEuE+Su(3)

如果对流项采用一阶迎风格式,则系数为
{ a W = D w + m a x ( F w , 0 ) a E = D e + m a x ( 0 , − F e ) a P = a W + a E + ( F e − F w ) (4) \left \{ \begin{aligned} a_W &= D_w + max(F_w, 0)\\ a_E &= D_e + max(0, -F_e)\\ a_P &= a_W + a_E + (F_e-F_w) \end{aligned} \right. \tag{4} aWaEaP=Dw+max(Fw,0)=De+max(0,Fe)=aW+aE+(FeFw)(4)

因为流动是无粘的,所以 D w = D e = 0 D_w = D_e = 0 Dw=De=0 F e F_e Fe F w F_w Fw是通过边界的质量通量,使用边界上的平均速度计算。源项 S u S_u Su只包含压力梯度项,根据几何关系有
S u = Δ p Δ x × Δ V = Δ p Δ x × A a v Δ x = Δ p × 1 2 ( A w + A e ) (5) \begin{aligned} S_u=\frac{\Delta p}{\Delta x} \times \Delta V = \frac{\Delta p}{\Delta x}\times A_{av} \Delta x = \Delta p \times \frac{1}{2} (A_w + A_e) \end{aligned} \tag{5} Su=ΔxΔp×ΔV=ΔxΔp×AavΔx=Δp×21(Aw+Ae)(5)

则离散方程的系数可以简化如下计算:
{ F w = ρ A w u w , F e = ρ A e u e a W = F w a E = 0 a P = a W + a E + ( F e − F w ) S u = Δ p + 1 2 ( A w + A e ) = Δ p × A P (6) \left \{ \begin{aligned} F_w &=\rho A_w u_w, F_e = \rho A_e u_e\\ a_W &=F_w\\ a_E &=0\\ a_P &=a_W + a_E + (F_e -F_w)\\ S_u &= \Delta p + \frac{1}{2}(A_w + A_e)=\Delta p \times A_P \end{aligned} \right. \tag{6} FwaWaEaPSu=ρAwuw,Fe=ρAeue=Fw=0=aW+aE+(FeFw)=Δp+21(Aw+Ae)=Δp×AP(6)

压力修正方程中的系数
d = A a v a P = ( A w + A e ) 2 a P (7) d=\frac{A_{av}}{a_P}= \frac{(A_w+A_e)}{2 a_P} \tag{7} d=aPAav=2aP(Aw+Ae)(7)

压力修正方程

连续性方程 ( 1 − a ) (1-a) (1a)离散后为
( ρ u A ) e − ( ρ u A ) w = 0 (8) \begin{aligned} (\rho u A)_e - (\rho u A)_w = 0 \end{aligned} \tag{8} (ρuA)e(ρuA)w=0(8)
对应的压力修正方程为
a P p P ′ = a W p W ′ + a E p E ′ + b ′ (9) \begin{aligned} a_P p^\prime _P = a_W p^\prime _W + a_E p^\prime _E + b^\prime \end{aligned} \tag{9} aPpP=aWpW+aEpE+b(9)
系数为
{ a W = ( ρ d A ) w a E = ( ρ d A ) e a P = a W + a E b ′ = ( F w ∗ − F e ∗ ) (10) \left \{ \begin{aligned} a_W&=(\rho d A)_w\\ a_E &= (\rho d A)_e\\ a_P &= a_W + a_E\\ b^\prime &=(F^*_w - F^*_e) \end{aligned} \right. \tag{10} aWaEaPb=(ρdA)w=(ρdA)e=aW+aE=(FwFe)(10)

其中参数 d d d即公式 ( 7 ) (7) (7) d d d

速度和压力的修正

得到压力修正量 p ′ p^\prime p后,速度修正量 u ′ u^\prime u以及压力场和速度场的修正用如下公式计算:
{ u ′ = d ( p I ′ − p I + 1 ′ ) p = p ∗ + p ′ u = u ∗ + u ′ (11) \left \{ \begin{aligned} u^\prime &=d(p^\prime _I - p^\prime _{I+1})\\ p &=p^* + p^\prime\\ u &=u^*+u^\prime \end{aligned} \right. \tag{11} upu=d(pIpI+1)=p+p=u+u(11)

边界节点的处理

入口:速度节点1

入口给定的滞止压强 p 0 = 10 P a p_0=10Pa p0=10Pa,可以认为是入口A上游压力室的压强,它等于流体流速为0时的静压值,但A处的实际速度不是0,流动控制方程中的压力指的是静压,因此需要先确定入口A处的静压。这里我们使用伯努利方程来计算:
p A = p 0 − 1 2 ( ρ u A 2 ) (12) \begin{aligned} p_A = p_0 - \frac{1}{2}(\rho u^2_A) \end{aligned} \tag{12} pA=p021(ρuA2)(12)
其中 u A u_A uA 是入口A处的速度值,也是未知量。根据质量守恒,入口速度 u A u_A uA可以按如下方式计算
u A = u 1 A 1 / A A (13) \begin{aligned} u_A = u_1 A_1/A_A \end{aligned} \tag{13} uA=u1A1/AA(13)
将公式 ( 13 ) (13) (13)代入公式 ( 12 ) (12) (12)
p A = p 0 − 1 2 ρ u 1 2 ( A 1 A A ) 2 (14) \begin{aligned} p_A = p_0 - \frac{1}{2} \rho u^2_1 \left( \frac{A_1}{A_A} \right)^2 \end{aligned} \tag{14} pA=p021ρu12(AAA1)2(14)
那么节点1的离散动量方程为
F e u 1 − F w u A = ( p A − p B ) × A 1 (15) \begin{aligned} F_e u_1 - F_w u_A = (p_A - p_B) \times A_1 \end{aligned} \tag{15} Feu1FwuA=(pApB)×A1(15)
根据公式 ( 13 ) (13) (13) F w = ρ u A A A = ρ u 1 A 1 F_w=\rho u_A A_A=\rho u_1 A_1 Fw=ρuAAA=ρu1A1,把公式 ( 13 ) 、 ( 14 ) (13)、(14) (13)(14)代入到公式 ( 15 ) (15) (15)
F e u 1 − F w u 1 A 1 / A A = [ ( p 0 − 1 2 ρ u 1 2 ( A 1 / A A ) 2 ) − p B ] × A 1 (16) \begin{aligned} F_e u_1 - F_w u_1 A_1 / A_A = [(p_0 - \frac{1}{2} \rho u_1^2(A_1/A_A)^2) - p_B] \times A_1 \end{aligned} \tag{16} Feu1Fwu1A1/AA=[(p021ρu12(A1/AA)2)pB]×A1(16)

重新整理一下顺序,得
[ F e − F w A 1 / A A + F w × 1 2 ( A 1 / A A ) 2 ] u 1 = ( p 0 − p B ) A 1 (17) \begin{aligned} [F_e - F_w A_1/A_A + F_w \times \frac{1}{2} (A_1/A_A)^2] u_1 = (p_0 - p_B)A_1 \end{aligned} \tag{17} [FeFwA1/AA+Fw×21(A1/AA)2]u1=(p0pB)A1(17)


这个整理过程可能有点儿绕,感觉需要解释一下,从公式 ( 16 ) (16) (16)到公式 ( 17 ) (17) (17)的推导步骤如下:

  1. 把等号右边方括号中的第二项独立出来,即 − 1 2 ρ u 1 2 ( A 1 / A A ) 2 × A 1 -\frac{1}{2} \rho u_1^2 (A_1/A_A)^2 \times A_1 21ρu12(A1/AA)2×A1;
  2. 把这一项换个造型,就成了 − 1 2 × ρ u 1 A 1 × u 1 ( A 1 / A A ) 2 -\frac{1}{2} \times \rho u_1A_1 \times u_1(A_1/A_A)^2 21×ρu1A1×u1(A1/AA)2;
  3. 利用 F w = ρ u 1 A 1 F_w = \rho u_1 A_1 Fw=ρu1A1,代入这一项中并放到等号右边,就是 F w × 1 2 ( A 1 / A A ) 2 u 1 F_w \times \frac{1}{2}(A_1/A_A)^2 u_1 Fw×21(A1/AA)2u1

这里将 F w F_w Fw看作为系数,就像离散方程中的 a a a一样,虽然它的公式里也包含了 u u u,在离散方程的求解过程中将使用初始速度值或上一步的迭代值来计算系数的值。


因此,节点1的主系数 a P a_P aP
a P = F e − F w A 1 / A A + F w × 1 2 ( A 1 / A A ) 2 (18) \begin{aligned} a_P = F_e - F_w A_1/A_A + F_w \times \frac{1}{2} (A_1/A_A)^2 \end{aligned} \tag{18} aP=FeFwA1/AA+Fw×21(A1/AA)2(18)

上式中等号右边的前两项来自动量方程的对流项,第三项是因在计算压力梯度项时使用了 ( w ) (w) (w)边界上的滞止压力而导致的额外项。在计算内部节点时可以直接使用静压,这一项是不存在的。

公式 ( 17 ) (17) (17)的形式可以直接用于计算,但更好的选择是将 a P a_P aP中的负项拿到右边的源项中,即
[ F e + F w × 1 2 ( A 1 / A A ) 2 ] u 1 = ( p 0 − p B ) A 1 + F w A 1 / A A × u 1 o l d (19) \begin{aligned} [F_e + F_w \times \frac{1}{2}(A_1/A_A)^2]u_1 = (p_0 - p_B)A_1 + F_w A_1/A_A \times u_1^{old} \end{aligned} \tag{19} [Fe+Fw×21(A1/AA)2]u1=(p0pB)A1+FwA1/AA×u1old(19)

这种操作叫做延迟修正法(deferred correction approach),可以提高计算的稳定性。

出口:速度节点4

出口是压力边界,边界条件给的就是静压值,可以直接使用,因此只需要解决出口通量计算问题就OK了。和入口一样,这里也是利用质量守恒关系,
F e = ( ρ u A ) 4 (20) \begin{aligned} F_e = (\rho u A)_4 \end{aligned} \tag{20} Fe=(ρuA)4(20)

计算结果

通过计算发现,网格数太少会导致结果精度不够,数值解和理论解相差较大。增加网格数量就可以明显改善这个问题,但增加网格数后需要适当降低松弛因子,否则计算可能会发散,具体计算结果见下图。

网格数:5

有限体积法(13)——SIMPLE算法的一维算例_第3张图片
有限体积法(13)——SIMPLE算法的一维算例_第4张图片

网格数:15

有限体积法(13)——SIMPLE算法的一维算例_第5张图片
有限体积法(13)——SIMPLE算法的一维算例_第6张图片

网格数:25

有限体积法(13)——SIMPLE算法的一维算例_第7张图片
有限体积法(13)——SIMPLE算法的一维算例_第8张图片

计算程序

import numpy as np
import matplotlib.pyplot as plt
import math
import sys

#== parameters ===
u_nodes_num = p_nodes_num =  5  # number of mesh cells or nodes
alpha = 0.8  # under-relaxation factor
nmax = 2000  # max number of iteration
residual_criteria = 1e-6

length = 2
density = 1
area_A = 0.5
area_E = 0.1
p_0 = 10  # inlet pressure
p_e = 0   # outlet pressure
#===================
# ==== creating mesh ======
xp_grid = np.linspace(0,length,p_nodes_num, endpoint=True) # pressure mesh
print('xp_grid = ',xp_grid)

xu_grid = np.zeros(u_nodes_num) # velocity mesh
for i in range(1,u_nodes_num):
    xu_grid[i] = (xp_grid[i] + xp_grid[i-1])/2
print('xu_grid = ',xu_grid)
#===== preparing constant values ======
area_I = np.linspace(area_A, area_E, p_nodes_num, endpoint=True) 
# area of sections A,B,C,D,E  ( Pressure nodes )

area_i = np.zeros(u_nodes_num) 
for i in range(1,u_nodes_num):
    area_i[i] = (area_I[i] + area_I[i-1])/2
# area of sections of 1,2,3,4 ( Velocity nodes )

# === coefficients =====
au = np.zeros(u_nodes_num)
dd = np.zeros(u_nodes_num)
bb = np.zeros(p_nodes_num)
Aap = np.zeros((p_nodes_num, p_nodes_num))
Bbp = np.zeros(p_nodes_num)


'''
# === display area  ===
plt.xlabel('Distance (m)')
plt.ylabel('A')
plt.plot(xu_grid[1:], area_i[1:] ,'bs', label='area_i')
plt.plot(xp_grid, area_I,'go-', label='area_I')
#plt.title('')
plt.legend()
plt.show()
sys.exit()
'''


#== initialization ==
guessedMassFlowRate = 1  # mass flow rate

uu = np.zeros(u_nodes_num)   # velocity , the index starts with 1
uu[1:] = guessedMassFlowRate/density/area_i[1:]   # initial velocity

uu0 = uu.copy()  # 
uu_residual = np.zeros(u_nodes_num) # residual of velcity

pp = np.linspace(p_0, p_e, p_nodes_num, endpoint=True) # initial pressure

print('initial u = ',uu)
print('initial p = ',pp)
# ==================

#== SIMPLE ALGORITHM ==

for j in range(nmax):
    ##== STEP 1 : solving momentum equations ==
    #== inlet ==
    i = 1
    fw = density * uu[i] * area_i[i]  
    fe = density * area_I[i] * (uu[i] + uu[i+1])/2
    ai = fe + 0.5*fw*(area_i[i]/area_A)**2          #ap
    su = (p_0 - pp[i])*area_i[i] + fw * area_i[i]/area_A * uu[i]  # source term
    uu_residual[i] = abs(ai*uu[i] - su)
    uu[i] = su / ai
    dd[i] = area_i[i]/ai
    #print(uu[i], uu0[i])

    #== internal nodes ==
    for i in range(2,u_nodes_num):
        aw = density * area_I[i-1] * (uu[i]+uu0[i-1])/2   # aw = Fw = (rho * u * A)_w
                                                          # ae = 0
        if i == u_nodes_num-1:
            ai = density * area_i[i] * uu[i]  # outlet
        else:
            ai = density * area_I[i] * (uu[i]+uu[i+1])/2   # ap = aw + ae + (Fe - Fw) 
                                                           #    = Fw + Fe - Fw 
                                                           #    = Fe
                                                           #    = (rho * u * A)_e 

        dd[i] = area_i[i]/ai  # d = a_av / ap
        uu_residual[i] = abs(ai*uu[i] - aw*uu[i-1] - area_i[i]*(pp[i-1]-pp[i]) )
            # according to formula (3), assume ap * up = aw * uw + ae * ue + su + residual. 
            # so, residual = ap * up - aw * uw - ae * ue - su 

        uu[i] = aw/ai * uu[i-1] + dd[i] * (pp[i-1]-pp[i])  # up = aw/ap * uw + su/ap
                                                           #    = aw/ap * uw + d * Delta p

    #print(uu[1:])

    ##== STEP 2: solving pressure correction equation ==
    for I in range(1,p_nodes_num-1):
        Aap[I,I-1] = -density * dd[I] * area_i[I]
        Aap[I,I+1] = -density * dd[I+1] * area_i[I+1]
        bb[I] = density * (area_i[I]*uu[I] - area_i[I+1]*uu[I+1])
        Aap[I,I] = -(Aap[I,I-1] + Aap[I,I+1])

    for i in [0, p_nodes_num-1]:  # inlet and outlet
        Aap[i,i] = 1
        bb[i] = 0

    pp_correction = np.linalg.solve(Aap, bb)  # solve pressure correction equations
    #print(pp_correction)

    ##== STEP 3: correct presure and velocity ==
    for i in range(1,u_nodes_num):
        uu[i] = uu[i] + alpha *dd[i]*(pp_correction[i-1] - pp_correction[i]) 

    pp = pp + alpha *pp_correction
    pp[0] = p_0 - 0.5*density*uu[1]**2 * (area_i[1]/area_A)

    uu0 = uu.copy()
    if j%100==0:
        print('i = ',j, '   u_residul = ', uu_residual.sum())

    if uu_residual.sum() < residual_criteria:
        print('i = ',j, '   u_residul = ', uu_residual.sum())
        break
    if uu_residual.sum() > 1e10:
        print('\ni = ',j, '   u_residul = ', uu_residual.sum(),' @(&_&)@  !!!!!!!!')
        print('Please reduce the under-relaxation factor (alpha) !\n')
        sys.exit()
        

print('mass flow rate = ', density*uu[-1]*area_i[-1])

##== exact solution ==
nodes_excat = 50
area_exact = np.linspace(area_A, area_E, nodes_excat, endpoint=True)
xe_grid = np.linspace(0, length, nodes_excat, endpoint=True)
pp_exact = np.zeros(nodes_excat)
uu_exact = np.zeros(nodes_excat)
for i in range(nodes_excat):
    pp_exact[i] = p_0 - 0.5 * 0.44721**2 /density / area_exact[i]**2
    uu_exact[i] = math.sqrt((p_0 - pp_exact[i])*2/density)


plt.xlabel('Distance (m)')
plt.ylabel('Velocity (m/s)')
plt.plot(xu_grid[1:], uu[1:] ,'bs--', label='Numerical')
plt.plot(xe_grid, uu_exact,'k', label='Exact')
plt.title('velocity')
plt.legend()


plt.figure(2)
plt.xlabel('Distance (m)')
plt.ylabel('Pressure (Pa)')
plt.plot(xp_grid, pp ,'bs--', label='Numerical')
plt.plot(xe_grid, pp_exact,'k', label='Exact')
plt.title('Pressure')
plt.legend()

plt.show()

参考资料

  1. Versteeg H K , Malalasekera W . An introduction to computational fluid dynamics : the finite volume method = 计算流体动力学导论[M]. 世界图书出版公司, 2010.

你可能感兴趣的:(CFD,cfd,流体力学)