工学系大学院生のブログ

2人の大学院生による雑記ブログ

第5-3回 オイラー方程式(Sod shock tube)[python]

$$\frac{\partial Q}{\partial t}+ \frac{\partial F}{\partial x} =0$$

$$Q=\left\{ \begin{array}{c} \rho \\ \rho u \\ e \end{array}\right\} , F=\left\{ \begin{array}{c} \rho u \\ p+\rho u^2 \\ (e+p)u \end{array}\right\} $$


今回は今までのまとめとして、Sod shock tubeと呼ばれる衝撃波管に対して数値解析を行います。この衝撃波管は厳密解が求められており、数値解の検証によく使われます


pythonのコードは最後に書いておきます。



衝撃波管の条件は以下の通りです。

$$\left(\begin{array}{c} u_L\\rho_L\\ p_L \end{array}\right) =\left(\begin{array}{c} 0.0\\1.0\\1.0 \end{array}\right) , \left(\begin{array}{c} u_R\\rho_R\\ p_R \end{array}\right) =\left(\begin{array}{c} 0.0\\0.125\\0.1 \end{array}\right) $$



具体的な解析内容は以下の通りです。

時間ステップ数 : \(nstep=300\)
時間刻み幅 : \(dt=0.001\)
x方向に取るセル数 : \(nx0=100\)
x方向の刻み幅 : \(dx=0.01\)
初期条件 : \(Q(x)=Q_L (x \leq 0.5), Q(x)=Q_R (x>0.5)\)
境界条件 : \(Q(bound_L)=Q_L, \frac{\partial Q(bound_R)}{\partial x}=0\)
比熱比:\(\gamma=1.4\)



結果になります。
0.3秒まで0.001刻みの時間履歴になります。




続いて\(t=2s\)における解析解と数値解の比較になります。

青:解析解  赤:数値解


sod shock tubeの解析解は計算しておらず、比較のため重ねただけです。


急襲に値の変化している場所では、なまりが大きく定性的に一致したといった結論が得られます。



境界の計算方法等、コードについて不足分を次の記事にまとめたので、そちらと合わせて確認いただけたらと思います。よろしくお願いします。


だいぶ物理現象を形にできてきている気がして、楽しかったです。

補足を第5-4回に記載しています。


by hide


*コードを見るときは,エディターに貼り付けてみた方が見やすいです。

import numpy
import os
import matplotlib.pyplot as pyplot

# --------------------------
# -- initial value        --
# --------------------------
nstep = 300                  # 時間ステップ数
nx0 = 100                    # 空間ステップ数
dt = 0.001                   # 時間刻み幅
dx = 0.01                    # 空間刻み幅

lpml = 1                     # 仮想境界セル数
nx = nx0+2*lpml              # 総空間セル数

# --定数--
gamma = 1.4    # 比熱比

# -- 出力--
dir_name = "FVS_test"        # 出力フォルダ名

# --------------------------
# -- function             --
# --------------------------

def setup():  # 初期値入力
    global x, bol, bor, qf, Qc
    u = [0.0]*nx               # 速度
    rho = [0.0]*nx             # 密度
    p = [0.0]*nx               # 圧力
    e = [0.0]*nx               # エネルギー
    x = [0.0]*nx               # 位置

    """
    e=p/(r-1)+rho*u^2/2
    """
    for i in range(nx):
        u[i] = 0.0

        if i <= nx*0.5:
            rho[i] = 1.0
            p[i] = 1.0

        else:
            rho[i] = 0.125
            p[i] = 0.1

        e[i] = p[i]/(gamma-1)+rho[i]*(u[i]**2)/2
        x[i] = i*dx-dx/2


    bol = [0.0]*3             # 左端仮想セル
    bor = [0.0]*3             # 右端仮想セル
    for j in range(3):
        if j == 0:
            bol[j] = rho[0]
            bor[j] = rho[nx-1]
        elif j == 1:
            bol[j] = u[0]*rho[0]
            bor[j] = u[nx-1]*rho[nx-1]
        elif j == 2:
            bol[j] = e[0]
            bor[j] = e[nx-1]

    qf = [[0.0] * 3 for i in [1] * nx]  # 基本量
    Qc = [[0.0] * 3 for i in [1] * nx]  # 保存量
    for i in range(nx):
        for j in range(3):
            if j == 0:
                qf[i][j] = u[i]
                Qc[i][j] = rho[i]
            elif j == 1:
                qf[i][j] = rho[i]
                Qc[i][j] = u[i]*rho[i]
            elif j == 2:
                qf[i][j] = p[i]
                Qc[i][j] = e[i]


def cal_Q():  # 保存量を計算
    global Qc
    cal_Res()                         # 保存量計算用にまとめる

    Qc = numpy.array(Qc)              # arrayに変換
    lo_R = numpy.array(Res)           # arrayに変換

    for i in range(1, nx-1):
        Qc[i] = Qc[i]-dt/dx*lo_R[i]   # メインの計算

    Qc.tolist()                       # listに戻す
    bound()                           # 境界の計算


def bound():  # 境界の計算
    global Qc
    for i in range(3):
        Qc[0][i] = 2*bol[i]-Qc[1][i]  # 左端境界の計算
        Qc[nx-1][i] = Qc[nx-2][i]     # 右端の計算


def cal_Res():  # 境界フラックスの計算
    global Res

    Res = numpy.array([[0.0] * 3 for i in [1] * nx])
    fvs()                             # FVS法によるフラックスの作成

    for i in range(1, nx-1):
        Res[i] = Fplus[i]-Fplus[i-1]

    Res.tolist()


def fvs():  # FVS法によるフラックスの計算(セル1と2の境界をFplus[1]に格納)
    global Fplus

    Fplus = [[0.0] * 3 for i in [1] * (nx+1)]

    for i in range(0, nx-1):
        # iセルにおけるR,R^-1,Λ,|Λ|
        R, R_inv, Gam, Gam_abs = A_pm(i)
        Ap = numpy.dot((numpy.dot(R, Gam+Gam_abs)), R_inv)               # 固有値が正のものを計算

        # i+1セルにおけるR,R^-1,Λ,|Λ|
        R, R_inv, Gam, Gam_abs = A_pm(i+1)
        Am = numpy.dot((numpy.dot(R, Gam-Gam_abs)), R_inv)               # 固有値が負のものを計算

        Fplus[i] = 0.5*(numpy.dot(Ap, Qc[i]) + numpy.dot(Am, Qc[i+1]))   # フラックスを計算


def A_pm(ite):  # ヤコビアン行列の固有値もろもろ計算
    H = (Qc[ite][2]+qf[ite][2])/Qc[ite][0]  # エンタルピー
    u = qf[ite][0]
    c = numpy.sqrt((gamma-1)*(H-0.5*u**2))
    b_para = (gamma-1)/c**2
    a_para = 0.5*b_para*u**2

    R = numpy.array([[1.0, 1.0, 1.0, ], [u-c, u, u+c],
                     [H-u*c, 0.5*u**2, H+u*c]])
    R_inv = numpy.array([[0.5*(a_para+u/c), 0.5*(-b_para*u-1/c), 0.5*b_para], [
                        1-a_para, b_para*u, -b_para], [0.5*(a_para-u/c), 0.5*(-b_para*u+1/c), 0.5*b_para]])
    Gam = numpy.array([[(u-c), 0.0, 0.0], [0.0, u, 0.0], [0.0, 0.0, (u+c)]])
    Gam_abs = numpy.array(
        [[abs(u-c), 0.0, 0.0], [0.0, abs(u), 0.0], [0.0, 0.0, abs(u+c)]])

    return R, R_inv, Gam, Gam_abs


def qftoQc(qf):  # 基本量から保存量変換
    lo_Qc = [[0.0] * 3 for i in [1] * nx]
    for i in range(nx):
        for j in range(3):
            if j == 0:
                lo_Qc[i][j] = qf[i][1]
            elif j == 1:
                lo_Qc[i][j] = qf[i][1]*qf[i][0]
            elif j == 2:
                lo_Qc[i][j] = (qf[i][2]/(gamma-1) + 1.0/2.0*qf[i][1]*(qf[i][0]**2))

    return lo_Qc


def Qctoqf(Qc):  # 保存量から基本量変換
    lo_qf = [[0.0] * 3 for i in [1] * nx]
    for i in range(nx):
        for j in range(3):
            if j == 0:
                lo_qf[i][j] = Qc[i][1]/Qc[i][0]
            elif j == 1:
                lo_qf[i][j] = Qc[i][0]
            elif j == 2:
                lo_qf[i][j] = (gamma-1)*(Qc[i][2] - 1.0/2.0*Qc[i][0]*((Qc[i][1]/Qc[i][0])**2))
    return lo_qf


def output_q(odir9, f_name9, q9, x9):  # テキスト形式で出力
    outlist = ["x[m] u[m/s] rho[kg/m3] p[Pa]"]  # 出力するものの名前
    for i in range(len(q9)):
        outlist.append(str(x9[i])+" "+str(q9[i][0]) +
                       " "+str(q9[i][1])+" "+str(q9[i][2]))
    outlist = '\n'.join(outlist)

    with open(odir9+"/"+f_name9, 'wt') as f:
        f.write(outlist)


def cre_dir():  # フォルダ作成
    try:
        os.mkdir(dir_name)
    except:
        pass


# --------------------------
# -- main                 --
# --------------------------
cre_dir()  # フォルダ作成
setup()    # 初期値計算

for k in range(nstep):
    print(k)
    cal_Q()               # 保存量計算
    qf = Qctoqf(Qc)       # 保存量から基本量へ変換

    output_q(dir_name, "time"+'{:0=4}'.format(int(k))+"d-3", qf, x)  # 出力

このエントリーをはてなブックマークに追加

コメント

  • Ӏ will immediately grab your rss as I can not tо find ʏߋur e-mail subscription link оr е-newsletter service.
    Ɗo you’ve any? Kindlly permit me relize iin ordeг that
    I coսld subscribe. Ƭhanks.

  • 解析解の計算方法について教えて頂けないでしょうか?
    よろしくお願い致します.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

トラックバックURL: https://teru-hide.com/cfd-python-euler-eq-sod-shock-tube/trackback/
にほんブログ村 英語ブログへ
にほんブログ村