数值分析 - 对流项的离散

对流项的数值离散

扩散项的离散在先前已经介绍,对于动量方程,更为棘手的问题在于N-S方程中对流项和压力项的离散处理。

在不可压流场的数值求解过程中,最重要的问题均是这两个一阶偏导项所引起的。

这里主要讨论关于对流-扩散项的离散方法。

对流项的离散方法

首先是关于对流项的两种离散思路的介绍

  1. Taylor展开离散

    Taylor展开的方法,就是直接套用泰勒公式将偏导转化为线性离散

  2. 控制容积积分

    通过将对流项的一阶导数在控制容积P中进行积分,利用相邻两个结点的值来获得控制容积边界e,w处的插值

两种思路所给出的截断误差的阶数是相同的

一维稳态对流-扩散的离散

由于整个N-S方程还是过于复杂,这里我们对整个物理问题可以进行相应的简化,从仅含有对流项和扩散的场景出发,来讨论如何解决对流项中的一阶导离散问题。

因此,首先对于一维稳态无内热源的对流扩散问题的数学描述,我们可以很轻松的给出方程的守恒形式:

\[\frac{d}{dx}(\rho u \phi) = \frac{d}{dx}(\Gamma \frac{d \phi}{dx}) \tag{1} \]

边界条件写为:

\[\phi(0) = \phi_0; \phi(L) = \phi_L \tag{1.1} \]

值得注意的是,以下内容表示的是对对流项(convection term)采用不同的差分格式进行描述,而公式中的扩散项仍然保持中心差分格式。

中心差分格式

采用控制容积法,对流项的中心差分格式相当于在控制容积P的界面上取分段的线性函数,即

\[(\rho u \phi)_e - (\rho u \phi)_w = (\rho u )_e \frac{\phi_E +\phi_P}{2}-(\rho u )_w \frac{\phi_P + \phi_W}{2} \tag{2.1} \]

最终的离散结果可以写为:

\[\phi_P[ \frac{(\rho u)_e}{2} - \frac{(\rho u)_w}{2} + \frac{\Gamma_e}{(\delta x)e} + \frac{\Gamma_w}{(\delta x)w}] = \phi_E(\frac{\Gamma_e}{(\delta x)_e} - \frac{(\rho u)_e}{2}) + \phi_W(\frac{\Gamma_w}{(\delta x)_w} +\frac{(\rho u)_w}{2}) \tag{2} \]

这里可以将界面上的流量记为$ F = \rho u\(,界面上单位扩散阻力的倒数记为\)D = \frac{\Gamma}{\delta x}$

最终式\((2)\)可以化简为:

\[a_P \phi_P = a_E \phi_E + a_W \phi_W \tag{2.2} \]

python代码可以轻松实现中心差分算法的描述:

# 中心差分格式(x结点数,y结点数,x方向扩散,y方向扩散,x界面流量,y界面流量,linux下debug符号)
def CDiff(x_nodes,y_nodes,D_x, D_y, F_x, F_y, Boundary, debug="n"):
    """
    Requires:
    x_nodes = number of nodes along x 
    y_nodes = number of nodes along y
    D_x = Diffusivity along x, type = float
    D_y = Diffusivity along y, type = float
    F_x = Force along X, type = matrix (x by y)
    F_y = Force along Y, type = matrix (x by y)
    Optional:
    debug, "y" if you want debug, off by default
    """
    ## setting up stuff (simple math)
    dim = x_nodes * y_nodes         # specified to reduce calculations
    w_bound = [0] * y_nodes         # creating 0-matricies to find
    e_bound = [0] * y_nodes         # all boundary nodes
    s_bound = [0] * x_nodes
    n_bound = [0] * x_nodes

    if debug == 'y':  # Debugger notifies progress            
        print("finding boundaries")

    for i in range (x_nodes):            # boundary nodes are populated
        s_bound[i] = i                   # used to populate matricies 
        n_bound[i] = (dim - x_nodes + i)
    for i in range (y_nodes):            
        w_bound[i] = (i * x_nodes)
        e_bound[i] = (i+1) * x_nodes -1

    if debug == 'y':   # Debugger prints boundary nodes
        xB = "n:" + str(n_bound) + "\n"+ " s:" + str(s_bound) 
        yB = "e:" + str(e_bound) + "\n"+ " w:" + str(w_bound) 
        print("boundaries: \n",  xB, "\n",  yB)
    # Declaring variables
    a_w        = [0] * dim              # Creating empty matricies for all nodes
    a_e        = [0] * dim
    a_s        = [0] * dim
    a_n        = [0] * dim
    sp_p       = 0
    su_p       = 0
    B          = [0] * dim
    sp_W       = [0] * dim
    su_W       = [0] * dim
    sp_E       = [0] * dim
    su_E       = [0] * dim
    sp_S       = [0] * dim
    su_S       = [0] * dim
    sp_N       = [0] * dim
    su_N       = [0] * dim

    #Boundaries
    phi_W = Boundary[0]
    phi_E = Boundary[1]
    phi_S = Boundary[2]
    phi_N = Boundary[3]

    ## answer matricies ##
    a = [0] * dim                      # make a 
    for i in range(dim):               # 0-array
        a[i] = [0] * dim
    B   = [0] * dim

    # POPULATING USING CENTRAL DIFFERENCE
    print("populating central difference")
    pbar = tqdm(range(dim))              # load progress bar
    for i in range(dim):
        pbar.update(1)                   # progress indicator
        if i not in  w_bound:       # populating non-boundary conditions
            a[i][i - 1]        = a[i][i - 1] - (D_x + (F_x[i - 1] / 2))
            a[i][i]            = a[i][i] + (D_x + (F_x[i - 1] / 2))
        if i not in  e_bound:
            a[i][i + 1]        = a[i][i + 1] - (D_x - (F_x[i + 1] / 2))
            a[i][i]            = a[i][i] + (D_x - (F_x[i + 1] / 2))
        if i not in  s_bound:
            a[i][i - x_nodes]  = a[i][i - x_nodes] - (D_y + (F_y[i - x_nodes] / 2))
            a[i][i]            = a[i][i] + (D_y + (F_y[i - x_nodes] / 2))
        if i not in  n_bound:
            a[i][i + x_nodes]  = a[i][i + x_nodes] - (D_y - (F_y[i + x_nodes] / 2))
            a[i][i]            = a[i][i] + (D_y - (F_y[i + x_nodes] / 2))
        if i in      w_bound:      # populating boundary conditions
            sp_W[i]            = -(2*D_x + F_x[i])
            su_W[i]            = (2*D_x + F_x[i])* phi_W
            a[i][i]            = a[i][i] - sp_W[i]
            B[i]               = B[i] + su_W[i]
        if i in      e_bound:
            sp_E[i]            = -(2*D_x - F_x[i])
            su_E[i]            = (2*D_x - F_x[i])* phi_E
            a[i][i]            = a[i][i] - sp_E[i]
            B[i]               = B[i] + su_E[i]
        if i in      s_bound:
            sp_S[i]            = -(2*D_y + F_y[i])
            su_S[i]            = (2*D_y + F_y[i])* phi_S
            a[i][i]            = a[i][i] - sp_S[i]
            B[i]               = B[i] + su_S[i]
        if i in      n_bound:
            sp_N[i]            = -(2*D_y - F_y[i])
            su_N[i]            = (2*D_y - F_y[i]) * phi_N
            a[i][i]            = a[i][i] - sp_N[i]
            B[i]               = B[i] + su_N[i]
    return a, B

迎风差分格式

利用控制容积法,对对流项的一阶迎风差分格式进行描述:

在e界面上 \(u_e > 0, \phi = \phi_P\) \(u_e<0,\phi = \phi_E\)
在w界面上 \(u_w > 0, \phi = \phi_W\) \(u_w < 0, \phi = \phi_P\)

因此对流迎风差分格式可以表达为:

\[(\rho u \phi)_e = F_e \phi_e = \phi_P MAX[F_e,0] - \phi_E MAX[-F_e,0] \tag{3.1} \]

最终的离散结果可写为:

\[\phi_P[ MAX[F_e,0] +MAX[-F_w,0] + \frac{\Gamma_e}{(\delta x)e} + \frac{\Gamma_w}{(\delta x)w}] \\ = \phi_E(\frac{\Gamma_e}{(\delta x)_e} + MAX[-F_e,0]) + \phi_W(\frac{\Gamma_w}{(\delta x)_w} +MAX[F_w,0]) \tag{3} \]

需要注意的是:

  • 在数值解不出现振荡的范围内,中心差分要比迎风差分结果的误差小
  • 一阶迎风格式在应用上误差较大,应用需由限制,但新发展出的二阶迎风、三阶迎风都从中吸取了思想

python代码中同样也可以实现一阶迎风差分算法的描述:

# 一阶迎风差分格式(x结点数,y结点数,x方向扩散,y方向扩散,x界面流量,y界面流量,linux下debug符号)
def UDiff(x_nodes,y_nodes,D_x, D_y, F_x, F_y, Boundary, debug="n"):
    """
    Requires:
    x_nodes = number of nodes along x 
    y_nodes = number of nodes along y
    D_x = Diffusivity along x, type = float
    D_y = Diffusivity along y, type = float
    F_x = Force along X, type = matrix (x by y)
    F_y = Force along Y, type = matrix (x by y)
    Optional:
    debug, "y" if you want debug, off by default
    """
    ## setting up stuff (simple math)
    dim = x_nodes * y_nodes         # specified to reduce calculations
    w_bound = [0] * y_nodes         # creating 0-matricies to find
    e_bound = [0] * y_nodes         # all boundary nodes
    s_bound = [0] * x_nodes
    n_bound = [0] * x_nodes

    if debug == 'y':  # Debugger notifies progress            
        print("finding boundaries")
    for i in range (x_nodes):            # boundary nodes are populated
        s_bound[i] = i                   # used to populate matricies 
        n_bound[i] = (dim - x_nodes + i)
    for i in range (y_nodes):            
        w_bound[i] = (i * x_nodes)
        e_bound[i] = (i+1) * x_nodes -1

    if debug == 'y':   # Debugger prints boundary nodes
        xB = "n:" + str(n_bound) + "\n"+ " s:" + str(s_bound) 
        yB = "e:" + str(e_bound) + "\n"+ " w:" + str(w_bound) 
        print("boundaries: \n",  xB, "\n",  yB)
 
    dim = x_nodes* y_nodes

    a_w		= [0] * dim              # Creating empty matricies for all nodes
    a_e 	= [0] * dim
    a_s 	= [0] * dim
    a_n 	= [0] * dim
    sp_p 	= 0
    su_p 	= 0
    B 		= [0] * dim
    sp_W 	= [0] * dim
    su_W 	= [0] * dim
    sp_E 	= [0] * dim
    su_E 	= [0] * dim
    sp_S 	= [0] * dim
    su_S 	= [0] * dim
    sp_N 	= [0] * dim
    su_N 	= [0] * dim

    #Boundaries
    phi_W = Boundary[0]
    phi_E = Boundary[1]
    phi_S = Boundary[2]
    phi_N = Boundary[3]

    ## answer matricies ##
    a = [0] * dim                      # make a 
    for i in range(dim):               # 0-array
        a[i] = [0] * dim
    B = [0] * dim
    # POPULATING USING CENTRAL DIFFERENCE
    print("populating upwind difference")
    pbar = tqdm(range(dim))
    for i in range(dim):
        pbar.update(1)
        if i not in w_bound:       		# populating non-boundary nodes
            a[i][i - 1]        = a[i][i - 1] - (D_x + max((F_x[i - 1]),0))
            a[i][i]            = a[i][i] + (D_x + max((F_x[i - 1]),0))
        if i not in e_bound:
            a[i][i + 1]        = a[i][i + 1] - (D_x + max(-(F_x[i + 1]),0))
            a[i][i]            = a[i][i] + (D_x + max(-(F_x[i + 1]),0))
        if i not in s_bound:
            a[i][i - x_nodes]  = a[i][i - x_nodes] - (D_y + max((F_y[i - x_nodes]),0))
            a[i][i]            = a[i][i] + (D_y + max((F_y[i - x_nodes]),0))
        if i not in n_bound: 
            a[i][i + x_nodes]  = a[i][i + x_nodes] - (D_y + max(-(F_y[i + x_nodes]),0))
            a[i][i]            = a[i][i] + (D_y + max(-(F_y[i + x_nodes]),0)) 
        if i in w_bound:           		# populating boundary nodes
            sp_W[i]            = -(2*D_x + max((F_x[i]),0))
            su_W[i]            = (2*D_x +  max((F_x[i]),0))* phi_W
            a[i][i]            = a[i][i] - sp_W[i] 
            B[i]               = B[i] + su_W[i]
        if i in e_bound:     
            sp_E[i]            = -(2*D_x + max(-(F_x[i]),0))
            su_E[i]            = (2*D_x + max(-(F_x[i]),0))* phi_E
            a[i][i]            = a[i][i] - sp_E[i]
            B[i]               = B[i] + su_E[i]
        if i in s_bound:     
            sp_S[i]            = -(2*D_y + max((F_y[i]),0))
            su_S[i]            = (2*D_y +  max((F_y[i]),0))* phi_S
            a[i][i]            = a[i][i] - sp_S[i]
            B[i]               = B[i] + su_S[i]
        if i in n_bound:     
            sp_N[i]            = -(2*D_y + max(-(F_y[i]),0))
            su_N[i]            = (2*D_x + max(-(F_y[i]),0))* phi_N
            a[i][i]            = a[i][i] - sp_N[i]
            B[i]               = B[i] + su_N[i]    
    return a, B

混合格式

乘方格式

指数格式

假扩散

上一篇:Auto-Encoding Variational Bayes (VAE原文)、变分推理


下一篇:P3768 简单的数学题