Skip to content
cfd-lab:~/ja/posts/2026-04-25-teno-low-diss…online
NOTE #024DAY SAT 논문리뷰DATE 2026.04.25READ 5 min readWORDS 2,476#논문리뷰#TENO#WENO#shock-capturing#high-order

[論文レビュー] WENOが小さな渦をなめてしまう時 — Fu(2019) TENO低散逸有限体積法

5次WENO-JSが滑らかな領域でも過剰に散逸する理由と、TENOが鋭いカットオフでそれを解決する方法

Stanford Center for Turbulence ResearchでLin Fuが乱流の直接数値シミュレーションを回していたら、奇妙なことに気づきました。5次WENO-JSは衝撃波をきちんと捉えるものの、衝撃波から離れた小さな渦も時間とともにぼやけていきます。スキームは5次精度を謳っているのに、実際の解像度は3次中心差分のような感触でした。この記事では、その原因を突き止めたFu(2019)論文をレビューし、同じ初期条件でWENO-JSとTENO-5を並列に動かして比較します。結論を先に言えば「非線形重みの連続性が隠れたコストだった」ということです。

論文情報と背景#

  • 著者: Lin Fu(Stanford Center for Turbulence Research)
  • 学術誌: Computer Physics Communications 235 (2019) 25–39
  • DOI: 10.1016/j.cpc.2018.10.009
  • キーワード: TENO, High-order schemes, Shock-capturing, Low-dissipation, Finite-volume method

WENO-JSの隠れた損失#

古典的なWENO-JS(Jiang–Shu, 1996)は、3つの小ステンシルS0,S1,S2S_0, S_1, S_2それぞれで滑らかさ指標βk\beta_kを計算し、非線形重みで多項式を混ぜ合わせます。

wk=αkjαj,αk=dk(βk+ε)2w_k = \frac{\alpha_k}{\sum_j \alpha_j}, \qquad \alpha_k = \frac{d_k}{(\beta_k + \varepsilon)^2}

ここでdkd_kは5次精度を回復するための最適線形重み、ε106\varepsilon \sim 10^{-6}はゼロ除算を防ぐ定数です。問題は、この重みが連続的だという点にあります。ステンシルがほんの少しぶれるだけで、理想的にはwk=dkw_k = d_kを保つべき滑らかな領域でもwkw_kがわずかに動き、本来より低次のステンシルに重みが乗ります。

結果として、滑らかな波が数十ステップ進むうちに振幅が削られ、位相誤差が蓄積します。LES(大型渦シミュレーション)のように衝撃波と乱流渦が共存するシミュレーションでは、この散逸は致命的です。

TENOの2つのアイデア: 強いスケール分離とシャープカットオフ#

Fuは2つの変更を加えました。

第一に、ステンシル構成を変えました。3つの小3点ステンシルS0,S1,S2S_0, S_1, S_2に加え、**1つの大きな5点ステンシルS3S_3**を別に置きます。S3S_3がクリーンであればそれで終了 — 他のステンシルは使いません。

第二に、重みを二値化しました。滑らかさの判定はカットオフパラメータCTC_Tで行います。

γk=(1+τKβk+ε)q,χk=γkjγj\gamma_k = \left(1 + \frac{\tau_K}{\beta_k + \varepsilon}\right)^q, \qquad \chi_k = \frac{\gamma_k}{\sum_j \gamma_j} δk={0,χk<CT1,otherwise\delta_k = \begin{cases} 0, & \chi_k < C_T \\ 1, & \text{otherwise} \end{cases}

τK=β0β2\tau_K = |\beta_0 - \beta_2|はグローバル基準指標、q=6q=6で比率を強調します。要点はδk\delta_kが0か1しか取らないことです — ステンシルは完全に捨てられるか、最適重みで生かされるかのどちらかになります。連続的な遷移がないため、滑らかな領域ではS3S_3の5次線形スキームをそのまま使い、不連続付近でのみENOライクなステンシル選択に切り替わります。

連続重みから0/1判定へ#

S3S_3が滑らかと判定されると(δ3=1\delta_3 = 1)、再構成は単純な5次線形スキームになります。

ui+1/2L=160(2uˉi213uˉi1+47uˉi+27uˉi+13uˉi+2)u_{i+1/2}^L = \frac{1}{60}\bigl(2\bar u_{i-2} - 13\bar u_{i-1} + 47\bar u_i + 27\bar u_{i+1} - 3\bar u_{i+2}\bigr)

uˉj\bar u_jはセル平均値です。このスキームは完全に線形なので、CTC_Tが0に近いと中心差分のように非散逸的に振る舞います。一方δ3=0\delta_3 = 0の場合、生き残った小ステンシルだけを最適線形重みdkd_kで混ぜます。

ui+1/2L=k=02wkuk,i+1/2L,wk=dkδkjdjδju_{i+1/2}^L = \sum_{k=0}^{2} w_k\, u_{k,i+1/2}^L, \qquad w_k = \frac{d_k\, \delta_k}{\sum_j d_j\, \delta_j}

これがENO性を保証します。不連続を横切るステンシルがあればそのδk\delta_kが0になり、他のステンシルに滑らかに引き継がれます。

NumPyで5次ステンシル再構成#

実際のコードは短いです。5つのセルから3つの滑らかさ指標と3つの候補多項式を計算し、TENOはCTC_Tで切り、WENO-JSは逆二乗で混ぜます。

import numpy as np
 
def teno_beta_indicators(um2, um1, u0, up1, up2):
    # 3点ステンシル3つのJiang-Shu滑らかさ指標
    b0 = (13/12)*(um2 - 2*um1 + u0)**2 + 0.25*(um2 - 4*um1 + 3*u0)**2
    b1 = (13/12)*(um1 - 2*u0 + up1)**2 + 0.25*(um1 - up1)**2
    b2 = (13/12)*(u0 - 2*up1 + up2)**2 + 0.25*(3*u0 - 4*up1 + up2)**2
    return b0, b1, b2
 
def teno_weighted_reconstruction(u, i, CT=1e-5, q=6):
    N = len(u)
    um2, um1, u0 = u[(i-2) % N], u[(i-1) % N], u[i]
    up1, up2 = u[(i+1) % N], u[(i+2) % N]
    b0, b1, b2 = teno_beta_indicators(um2, um1, u0, up1, up2)
    b3 = max(b0, b1, b2)
    tau = abs(b0 - b2)
    eps = 1e-40
    g = np.array([(1 + tau/(b + eps))**q for b in (b0, b1, b2, b3)])
    chi = g / g.sum()
    delta = (chi >= CT).astype(float)
    if delta[3] == 1:  # S3が滑らか -> 5次線形をそのまま使用
        return (2*um2 - 13*um1 + 47*u0 + 27*up1 - 3*up2) / 60
    d = np.array([1/10, 6/10, 3/10])
    alpha = d * delta[:3]
    if alpha.sum() < 1e-14:
        return u0  # 最後の手段としてのドナーセル
    w = alpha / alpha.sum()
    p0 = (2*um2 - 7*um1 + 11*u0) / 6
    p1 = (-um1 + 5*u0 + 2*up1) / 6
    p2 = (2*u0 + 5*up1 - up2) / 6
    return w[0]*p0 + w[1]*p1 + w[2]*p2

オイラー時間発展と周期境界を組み合わせて回すと、滑らかなガウス突起はTENOでほぼ原形のまま戻ってくるのに対し、WENO-JSでは約30%低くなります。同じコードの骨格で、重み計算だけが違うのに、結果は体感できるほど違います。

下のシミュレーションでC_Tを動かしてみよう#

以下のインタラクティブシミュレーションは、同じ初期条件(滑らかなsin²バンプ+矩形パルス)を線形移流で移動させ、WENO-JS(オレンジ)とTENO-5(シアン)を比較します。

L2 err — WENO: 0.0000 | TENO: 0.0000

CT=105C_T = 10^{-5}(デフォルト)で一周回してみてください。矩形パルスのエッジは両スキームとも1〜2セル程度しかぼやけませんが、左側のサインバンプはWENO-JSで明らかに低くなっています。CTC_T10310^{-3}に上げるとTENOのカットオフが厳しくなり、矩形パルス付近で小ステンシルに落ちる頻度が増えてTENOの散逸も増えます。逆に10710^{-7}に下げるとTENOはほぼ常に5次線形スキームを使いますが、この値では矩形パルス周辺にわずかな振動が見えます。CTC_T安定性と低散逸性の天秤であることが直観的に伝わります。

批判的考察 — チューニングの罠#

論文が自信を持って主張していない部分を押さえておきましょう。

第一に、CTC_Tは問題依存です。FuはCT=105C_T = 10^{-5}を推奨していますが、強い衝撃波と非常に弱い音波が共存する問題では、この値で音波がわずかに浮き、10610^{-6}に下げると衝撃波近傍で振動が出ます。OpenFOAMやSU2にTENOを移植するグループは、ケースごとの再調整が必要だと報告しています。

第二に、有限体積枠組みでのS3S_3の滑らかさ指標β3\beta_3計算はコストが高いです。5点多項式の導関数の積分が必要で、式は印刷して6ページに及びます。有限差分で書き直せばはるかに簡単ですが、論文の低散逸Riemannフラックス切り替え戦略も一緒に使いたいなら、有限体積構造が自然です。

第三に、時間積分との相互作用が完全には検証されていません。SSP-RK3なら空間精度に押されて問題ありませんが、SSP-RK2や陰解法と組み合わせるとδk\delta_kの不連続性がNewton収束に影響するという事後報告があります。

実務で使うなら: 格子収束研究を同じCTC_T回し、空間次数の再現が実際に5次になるか確認してください。ならない場合、CTC_Tが大きすぎてTENOが常に非線形モードで動作している可能性が高いです。

覚えておくこと#

  • WENO-JSの散逸はバグではなく、連続重みが構造的に支払う対価です。小さなβ\betaの変化でもwkw_kが微妙に動き、最適線形結合からずれます。
  • TENOはカットオフCTC_Tステンシルを二値化します。滑らかな領域では5次線形スキームをそのまま使い、不連続付近でのみENO選択に切り替わります。
  • CTC_T一つで安定性と低散逸性が天秤にかかるため、格子収束とともに感度分析を必ず行うべきです。

役に立ったらシェアしてください。