生成AIによるプログラム生成

GitHub Copilot (Claude)でCFDのコード生成

低マッハ数近似というOpenFOAMの伝熱系のソルバとかの基本になるモデルがあります。それ使ったソルバをGitHub Copilot上で生成AI(Claude Sonnet)を動かして,作ってみました。こっちは指示しか与えませんでした。パラメータの設定とかは修正しましたが基本ちゃんと動きます。ブジネスク近似が破綻するような温度差でも対応可。

高温度差の熱対流の解析コード (ここをクリックするとソースファイルの詳細が出ます)

"""
2次元レイリーベナール対流解析コード(Julia版・次元あり)
- 低マッハ数近似(Low-Mach Number Approximation)
- PISO法(Pressure Implicit with Splitting of Operators)
- スタガード格子
- 下壁:高温(T=350K),上壁:低温(T=300K)
- 全壁:すべりなし境界条件
- 作動流体:空気(理想気体)

【無次元版からの主な変更点】
1. 全物理量を次元あり(SI単位系)で扱う
2. 密度:ρ = p0 / (R * T)(理想気体,p0 は熱力学的圧力)
3. 粘性係数 μ,熱拡散係数 α は定数(参照温度での値)
4. 浮力項:(ρ - ρ_ref) * g
5. 圧力ポアソン:∇·(1/ρ ∇p') = (∇·u* - S_T) / dt
6. 密度変化ソース項:S_T = α ∇²T / T
"""

using Printf
using LinearAlgebra
using Statistics

# ============================================================
# 物理定数・流体物性(空気,参照温度 T_ref = 325 K)
# ============================================================

const g        = 9.81          # 重力加速度 [m/s²]
const R_gas    = 287.0         # 空気の気体定数 [J/(kg·K)]
const c_p      = 1005.0        # 定圧比熱 [J/(kg·K)]

# 温度条件
const T_hot    = 450.0         # 下壁温度 [K]
const T_cold   = 300.0         # 上壁温度 [K]
const T_init   = 300.0         # 初期温度 [K]
const T_ref    = 0.5 * (T_hot + T_cold)   # 参照温度 [K] = 325 K

# 参照状態(T_ref における空気物性)
const p0       = 101325.0      # 熱力学的圧力(大気圧,空間一様)[Pa]
const rho_ref  = p0 / (R_gas * T_ref)     # 参照密度 [kg/m³] ≈ 1.084
const mu       =  1.96e-5       # 動粘性率(粘性係数)[Pa·s]  (325K の空気)
const nu       = 1e-2 #mu / rho_ref  # 動粘性係数 [m²/s]
const lambda   = 0.0281        # 熱伝導率 [W/(m·K)]           (325K の空気)
const alpha    = 1.e-2 #lambda / (rho_ref * c_p)  # 熱拡散係数 [m²/s]

# 粘性係数が大きすぎると自然対流が起きない

# ============================================================
# 計算領域・格子
# ============================================================

const Lx = 2.0       # x方向長さ [m]
const Ly = 1.0       # y方向長さ [m](高さ)
const Nx = 64        # x方向セル数
const Ny = 32        # y方向セル数
const dx = Lx / Nx   # [m]
const dy = Ly / Ny   # [m]

const xc = [(i + 0.5) * dx for i in 0:Nx-1]   # セル中心 x座標 [m]
const yc = [(j + 0.5) * dy for j in 0:Ny-1]   # セル中心 y座標 [m]

# ============================================================
# 時間・ソルバパラメータ
# ============================================================

const dt             = 5e-4    # 時間刻み [s]
const t_end          = 10 #100.0  # 終了時間 [s]
const save_interval  = 1000     # 結果保存間隔(ステップ数)

const n_correctors    = 2      # PISO 圧力修正回数
const n_pressure_iter = 1500     # 圧力ポアソン反復数
const pressure_tol    = 0.05   # 圧力収束判定 [Pa]
const sor_omega       = 1.6    # SOR 緩和係数

# ============================================================
# 参照パラメータの表示用(レイリー数・プラントル数)
# ============================================================

const beta  = 1.0 / T_ref                          # 体積膨張係数(理想気体)[1/K]
const Ra    = g * beta * (T_hot - T_cold) * Ly^3 / (nu * alpha)
const Pr    = nu / alpha

# ============================================================
# 密度の計算
# 低マッハ数近似:ρ = p0 / (R * T)
# ============================================================

"""セル中心密度を計算(低マッハ数近似:ρ = p0 / (R_gas * T))[kg/m³]"""
function compute_density!(rho, T)
    @inbounds for j in 1:Ny, i in 1:Nx
        rho[i, j] = p0 / (R_gas * max(T[i, j], 1.0))   # ゼロ除算防止
    end
end

"""u面上の密度を線形補間 [kg/m³]"""
function interpolate_rho_u(rho)
    rho_u = fill(rho_ref, Nx+1, Ny)
    @inbounds for j in 1:Ny
        rho_u[1,    j] = rho[1,  j]
        rho_u[Nx+1, j] = rho[Nx, j]
        for i in 2:Nx
            rho_u[i, j] = 0.5 * (rho[i-1, j] + rho[i, j])
        end
    end
    return rho_u
end

"""v面上の密度を線形補間 [kg/m³]"""
function interpolate_rho_v(rho)
    rho_v = fill(rho_ref, Nx, Ny+1)
    @inbounds for i in 1:Nx
        rho_v[i, 1]    = rho[i, 1]
        rho_v[i, Ny+1] = rho[i, Ny]
        for j in 2:Ny
            rho_v[i, j] = 0.5 * (rho[i, j-1] + rho[i, j])
        end
    end
    return rho_v
end

# ============================================================
# 境界条件
# ============================================================

"""すべりなし・不透過境界条件"""
function apply_velocity_bc!(u, v)
    u[1,    :] .= 0.0
    u[Nx+1, :] .= 0.0
    v[:, 1]    .= 0.0
    v[:, Ny+1] .= 0.0
end

"""温度のゴーストセルを返す [K]
   下壁:T = T_hot(ディリクレ)
   上壁:T = T_cold(ディリクレ)
   左右壁:断熱(ノイマン)
"""
function get_T_extended(T)
    T_ext = zeros(Nx+2, Ny+2)
    T_ext[2:Nx+1, 2:Ny+1] .= T

    T_ext[2:Nx+1, 1]    .= 2.0 * T_hot  .- T[:, 1]    # 下壁
    T_ext[2:Nx+1, Ny+2] .= 2.0 * T_cold .- T[:, Ny]   # 上壁
    T_ext[1,    2:Ny+1] .= T[1,  :]                    # 左壁(断熱)
    T_ext[Nx+2, 2:Ny+1] .= T[Nx, :]                    # 右壁(断熱)
    T_ext[1,    1]    = T[1,  1]
    T_ext[Nx+2, 1]    = T[Nx, 1]
    T_ext[1,    Ny+2] = T[1,  Ny]
    T_ext[Nx+2, Ny+2] = T[Nx, Ny]

    return T_ext
end

# ============================================================
# 中間速度の計算
#
# u成分:ρ Du/Dt = -∂p/∂x + μ ∇²u
# v成分:ρ Dv/Dt = -∂p/∂y + μ ∇²v + (ρ - ρ_ref) * g  ← 浮力
#
# 離散化:u* = u + (dt / ρ) * RHS
# ============================================================

"""u速度の中間値(u*)を計算 [m/s]"""
function compute_u_star!(u_star, u, v, p, rho_u)
    fill!(u_star, 0.0)

    @inbounds for j in 1:Ny, i in 2:Nx
        u_c   = u[i, j]
        rho_f = rho_u[i, j]   # [kg/m³]

        # 対流項 [m/s²]
        u_e = (i < Nx) ? 0.5*(u[i, j] + u[i+1, j]) : 0.0
        u_w = (i > 2)  ? 0.5*(u[i-1, j] + u[i, j]) : 0.0
        conv_x = (u_e^2 - u_w^2) / dx

        if j < Ny
            v_n = 0.5*(v[i-1, j+1] + v[i, j+1])
            u_n = 0.5*(u[i, j] + u[i, j+1])
        else
            v_n = 0.0; u_n = 0.0
        end
        if j > 1
            v_s = 0.5*(v[i-1, j] + v[i, j])
            u_s = 0.5*(u[i, j-1] + u[i, j])
        else
            v_s = 0.0; u_s = 0.0
        end
        conv_y = (v_n*u_n - v_s*u_s) / dy

        # 拡散項(μ/ρ = ν)[m/s²]
        diff_x = (u[i+1, j] - 2*u[i, j] + u[i-1, j]) / dx^2
        u_jm1  = (j > 1)  ? u[i, j-1] : -u[i, j]
        u_jp1  = (j < Ny) ? u[i, j+1] : -u[i, j]
        diff_y = (u_jp1 - 2*u[i, j] + u_jm1) / dy^2

        # 圧力勾配項(1/ρ * ∂p/∂x)[m/s²]
        dp_dx = (p[i, j] - p[i-1, j]) / dx   # [Pa/m]

        u_star[i, j] = u_c + dt * (
            -(conv_x + conv_y)
            + nu * (diff_x + diff_y)
            - dp_dx / rho_f
        )
    end

    u_star[1,    :] .= 0.0
    u_star[Nx+1, :] .= 0.0
end

"""v速度の中間値(v*)を計算 [m/s]"""
function compute_v_star!(v_star, u, v, p, T, rho_v)
    fill!(v_star, 0.0)

    # 温度をv面へ線形補間 [K]
    T_v = zeros(Nx, Ny+1)
    @inbounds for j in 2:Ny
        T_v[:, j] .= 0.5 .* (T[:, j-1] .+ T[:, j])
    end
    T_v[:, 1]    .= T_hot    # 下壁
    T_v[:, Ny+1] .= T_cold   # 上壁

    @inbounds for j in 2:Ny, i in 1:Nx
        v_c   = v[i, j]
        rho_f = rho_v[i, j]   # [kg/m³]

        # 対流項 [m/s²]
        if i < Nx
            u_e = 0.5*(u[i+1, j-1] + u[i+1, j])
            v_e = 0.5*(v[i, j] + v[i+1, j])
        else
            u_e = 0.0; v_e = 0.0
        end
        if i > 1
            u_w = 0.5*(u[i, j-1] + u[i, j])
            v_w = 0.5*(v[i-1, j] + v[i, j])
        else
            u_w = 0.0; v_w = 0.0
        end
        conv_x = (u_e*v_e - u_w*v_w) / dx

        v_n = (j < Ny) ? 0.5*(v[i, j] + v[i, j+1]) : 0.0
        v_s = (j > 2)  ? 0.5*(v[i, j-1] + v[i, j]) : 0.0
        conv_y = (v_n^2 - v_s^2) / dy

        # 拡散項 [m/s²]
        v_im1 = (i > 1)  ? v[i-1, j] : -v[i, j]
        v_ip1 = (i < Nx) ? v[i+1, j] : -v[i, j]
        diff_x = (v_ip1 - 2*v[i, j] + v_im1) / dx^2
        diff_y = (v[i, j+1] - 2*v[i, j] + v[i, j-1]) / dy^2

        # 圧力勾配項 [m/s²]
        dp_dy = (p[i, j] - p[i, j-1]) / dy   # [Pa/m]

        # 浮力項:(ρ - ρ_ref) * g / ρ [m/s²]
        # 高温(低密度)→ ρ < ρ_ref → 上向き加速(正)
        # 低温(高密度)→ ρ > ρ_ref → 下向き加速(負)
        rho_v_face = p0 / (R_gas * max(T_v[i, j], 1.0))
        buoyancy = -(rho_v_face - rho_ref) * g / rho_f

        v_star[i, j] = v_c + dt * (
            -(conv_x + conv_y)
            + nu * (diff_x + diff_y)
            - dp_dy / rho_f
            + buoyancy
        )
    end

    v_star[:, 1]    .= 0.0
    v_star[:, Ny+1] .= 0.0
end

# ============================================================
# 発散の計算
# ============================================================

"""速度場の発散を計算 [1/s]"""
function compute_divergence!(div, u, v)
    @inbounds for j in 1:Ny, i in 1:Nx
        div[i, j] = (u[i+1, j] - u[i, j]) / dx +
                    (v[i, j+1] - v[i, j]) / dy
    end
end

# ============================================================
# 密度変化ソース項
# S_T = α ∇²T / T  [1/s]
#
# 導出:
#   ∇·u = (1/T) DT/Dt = (1/T)(α∇²T) = α∇²T / T
# ============================================================

"""密度変化ソース項 S_T = α∇²T / T を計算 [1/s]"""
function compute_density_source!(S_T, T)
    T_ext = get_T_extended(T)

    @inbounds for j in 1:Ny, i in 1:Nx
        # ∇²T(中心差分)[K/m²]
        lap_T = (T_ext[i+2, j+1] - 2*T_ext[i+1, j+1] + T_ext[i,   j+1]) / dx^2 +
                (T_ext[i+1, j+2] - 2*T_ext[i+1, j+1] + T_ext[i+1, j  ]) / dy^2

        # S_T = α ∇²T / T [1/s]
        S_T[i, j] = alpha * lap_T / max(T[i, j], 1.0)
    end
end

# ============================================================
# 圧力ポアソン方程式(SOR法)
# ∇·(1/ρ ∇p') = (∇·u* - S_T) / dt
# 単位:左辺 [1/(kg/m³) * Pa/m²] = [m/s² / m] = [1/s²]
#       右辺 [(1/s)] / [s]        = [1/s²]  ✓
# ============================================================

"""圧力修正量 p' を SOR法で求める [Pa]"""
function solve_pressure_poisson!(p_prime, rhs, rho)
    fill!(p_prime, 0.0)

    for iter in 1:n_pressure_iter
        max_res = 0.0

        @inbounds for j in 1:Ny, i in 1:Nx
            # ノイマン境界:ゴーストセル = 内側セル値
            p_e = (i < Nx) ? p_prime[i+1, j] : p_prime[i, j]
            p_w = (i > 1)  ? p_prime[i-1, j] : p_prime[i, j]
            p_n = (j < Ny) ? p_prime[i, j+1] : p_prime[i, j]
            p_s = (j > 1)  ? p_prime[i, j-1] : p_prime[i, j]

            # 面上密度(算術平均)[kg/m³]
            rho_e = (i < Nx) ? 0.5*(rho[i, j] + rho[i+1, j]) : rho[i, j]
            rho_w = (i > 1)  ? 0.5*(rho[i-1, j] + rho[i, j]) : rho[i, j]
            rho_n = (j < Ny) ? 0.5*(rho[i, j] + rho[i, j+1]) : rho[i, j]
            rho_s = (j > 1)  ? 0.5*(rho[i, j-1] + rho[i, j]) : rho[i, j]

            # 係数 [m²/kg]
            a_e = 1.0 / (rho_e * dx^2)
            a_w = 1.0 / (rho_w * dx^2)
            a_n = 1.0 / (rho_n * dy^2)
            a_s = 1.0 / (rho_s * dy^2)
            a_p = a_e + a_w + a_n + a_s

            p_new   = (a_e*p_e + a_w*p_w + a_n*p_n + a_s*p_s - rhs[i, j]) / a_p
            res     = abs(p_new - p_prime[i, j])
            max_res = max(max_res, res)
            p_prime[i, j] = (1.0 - sor_omega)*p_prime[i, j] + sor_omega*p_new
        end

        # 基準点固定(左下コーナー)
        p0_ref = p_prime[1, 1]
        p_prime .-= p0_ref

        if max_res < pressure_tol
            break
        end
        if iter == n_pressure_iter
            println("res= ", max_res)
        end
    end
end

# ============================================================
# 温度方��式(陽的,風上差分+中心差分)
# ∂T/∂t + u·∇T = α ∇²T
# ============================================================

"""温度方程式を1ステップ進める [K]"""
function advance_temperature!(T_new, u, v, T)
    T_ext = get_T_extended(T)

    @inbounds for j in 1:Ny, i in 1:Nx
        u_c = 0.5*(u[i, j] + u[i+1, j])   # [m/s]
        v_c = 0.5*(v[i, j] + v[i, j+1])   # [m/s]

        # 対流項(1次風上差分)[K/s]
        dTdx_p = (T_ext[i+2, j+1] - T_ext[i+1, j+1]) / dx
        dTdx_m = (T_ext[i+1, j+1] - T_ext[i,   j+1]) / dx
        dTdy_p = (T_ext[i+1, j+2] - T_ext[i+1, j+1]) / dy
        dTdy_m = (T_ext[i+1, j+1] - T_ext[i+1, j  ]) / dy

        conv = u_c * (u_c >= 0 ? dTdx_m : dTdx_p) +
               v_c * (v_c >= 0 ? dTdy_m : dTdy_p)

        # 拡散項(中心差分)[K/s]
        diff = alpha * (
            (T_ext[i+2, j+1] - 2*T_ext[i+1, j+1] + T_ext[i,   j+1]) / dx^2 +
            (T_ext[i+1, j+2] - 2*T_ext[i+1, j+1] + T_ext[i+1, j  ]) / dy^2
        )

        T_new[i, j] = T[i, j] + dt * (-conv + diff)
    end
end

# ============================================================
# PISOアルゴリズム(1ステップ)
# ============================================================

"""PISOアルゴリズムで1タイムステップ進める"""
function piso_step!(u, v, p, T, rho,
                    u_star, v_star, p_prime, div, S_T, T_new)

    # ステップ0:密度の更新 [kg/m³]
    compute_density!(rho, T)
    rho_u = interpolate_rho_u(rho)
    rho_v = interpolate_rho_v(rho)

    # ステップ1:中間速度(u*, v*)の計算 [m/s]
    compute_u_star!(u_star, u, v, p, rho_u)
    compute_v_star!(v_star, u, v, p, T, rho_v)
    apply_velocity_bc!(u_star, v_star)

    # 密度変化ソース項 [1/s]
    compute_density_source!(S_T, T)

    # ステップ2:圧力修正ループ(PISO)
    u_c = copy(u_star)
    v_c = copy(v_star)

    for _ in 1:n_correctors
        compute_divergence!(div, u_c, v_c)

        # 右辺 [1/s²]
        rhs = (div .- S_T) ./ dt

        solve_pressure_poisson!(p_prime, rhs, rho)

        p .+= p_prime

        # 速度修正:u -= dt/ρ * ∇p' [m/s]
        @inbounds for j in 1:Ny, i in 2:Nx
            u_c[i, j] -= (dt / rho_u[i, j]) * (p_prime[i, j] - p_prime[i-1, j]) / dx
        end
        @inbounds for j in 2:Ny, i in 1:Nx
            v_c[i, j] -= (dt / rho_v[i, j]) * (p_prime[i, j] - p_prime[i, j-1]) / dy
        end

        apply_velocity_bc!(u_c, v_c)
    end

    # 動的圧力の基準値固定(平均ゼロ)
    p .-= mean(p)

    u .= u_c
    v .= v_c

    # ステップ3:温度方程式を進める
    advance_temperature!(T_new, u, v, T)
    T .= T_new
end

# ============================================================
# ヌッセルト数の計算
# Nu = 1 + <v T> / (α ΔT / Ly)
# ============================================================

"""体積平均ヌッセルト数を計算"""
function compute_nusselt(T, v)
    v_c    = 0.5 .* (v[:, 1:Ny] .+ v[:, 2:Ny+1])   # [m/s]
    T_nd   = (T .- T_cold) ./ (T_hot - T_cold)       # 無次元温度(表示用)
    Nu     = 1.0 + mean(v_c .* T_nd) * Ly / alpha
    return Nu
end

# ============================================================
# 結果の出力(CSV)
# ============================================================

"""温度場・速度場・密度場をCSVに出力"""
function save_fields(u, v, p, T, rho, step)
    filename = @sprintf("rb_lowmach_dim_step%06d.csv", step)
    u_c = 0.5 .* (u[1:Nx, :] .+ u[2:Nx+1, :])
    v_c = 0.5 .* (v[:, 1:Ny] .+ v[:, 2:Ny+1])

    open(filename, "w") do io
        println(io, "x[m],y[m],u[m/s],v[m/s],p[Pa],T[K],rho[kg/m3]")
        for j in 1:Ny, i in 1:Nx
            @printf(io, "%.6f,%.6f,%.6e,%.6e,%.6e,%.4f,%.6f\n",
                xc[i], yc[j], u_c[i,j], v_c[i,j], p[i,j], T[i,j], rho[i,j])
        end
    end
end

"""ヌッセルト数履歴をCSVに出力"""
function save_nusselt(t_history, Nu_history)
    open("nusselt_history.csv", "w") do io
        println(io, "t[s],Nu")
        for (t, Nu) in zip(t_history, Nu_history)
            @printf(io, "%.4f,%.6f\n", t, Nu)
        end
    end
end

# ============================================================
# メインループ
# ============================================================

function main()
    println("=" ^ 65)
    println("2D Rayleigh-Bénard Convection (Low-Mach, Dimensional)")
    @printf("  Ra = %.3e,  Pr = %.3f\n", Ra, Pr)
    @printf("  Nx = %d,  Ny = %d,  Lx = %.1f m,  Ly = %.1f m\n", Nx, Ny, Lx, Ly)
    @printf("  dx = %.4f m,  dy = %.4f m\n", dx, dy)
    @printf("  dt = %.2e s,  t_end = %.1f s\n", dt, t_end)
    println("-" ^ 65)
    @printf("  T_hot = %.1f K,  T_cold = %.1f K,  T_init = %.1f K\n", T_hot, T_cold, T_init)
    @printf("  T_ref = %.1f K,  p0 = %.1f Pa\n", T_ref, p0)
    @printf("  ρ_ref = %.4f kg/m³\n", rho_ref)
    @printf("  ν     = %.3e m²/s\n", nu)
    @printf("  α     = %.3e m²/s\n", alpha)
    @printf("  λ     = %.4f W/(m·K)\n", lambda)
    println("=" ^ 65)

    # 安定性チェック
    u_est    = sqrt(g * (1.0/T_ref) * (T_hot - T_cold) * Ly)   # 自由対流速度スケール
    cfl      = u_est * dt / min(dx, dy)
    diff_num = max(nu, alpha) * dt / min(dx, dy)^2
    @printf("  u_scale = %.4f m/s\n", u_est)
    @printf("  CFL(est) = %.3f,  Diffusion number = %.3f\n", cfl, diff_num)
    if cfl > 0.5 || diff_num > 0.5
        println("  WARNING: Time step may be too large for stability!")
    end
    println()

    # 変数の初期化
    u   = zeros(Nx+1, Ny)     # [m/s]
    v   = zeros(Nx, Ny+1)     # [m/s]
    p   = zeros(Nx, Ny)       # 動的圧力 [Pa]
    T   = fill(T_init, Nx, Ny)  # [K]
    rho = zeros(Nx, Ny)       # [kg/m³]

    # 初期温度場:T_init = T_cold = 300 K + 微小擾乱
    #for j in 1:Ny, i in 1:Nx
    #    T[i, j] = T_init + 0.1 * randn()   # [K],擾乱スケール 0.1 K
    #end
    #clamp!(T, T_cold - 5.0, T_hot + 5.0)

    compute_density!(rho, T)

    # 作業バッファ
    u_star  = zeros(Nx+1, Ny)
    v_star  = zeros(Nx, Ny+1)
    p_prime = zeros(Nx, Ny)
    div     = zeros(Nx, Ny)
    S_T     = zeros(Nx, Ny)
    T_new   = zeros(Nx, Ny)

    t          = 0.0
    step       = 0
    Nu_history = Float64[]
    t_history  = Float64[]

    # ウォームアップ(JIT)
    #println("  JIT warmup...")
    #for _ in 1:5
    #    piso_step!(u, v, p, T, rho, u_star, v_star, p_prime, div, S_T, T_new)
    #end
    # リセット
    fill!(u, 0.0); fill!(v, 0.0); fill!(p, 0.0)
    #for j in 1:Ny, i in 1:Nx
    #    T[i, j] = T_init + 0.1 * randn()
    #end
    #clamp!(T, T_cold - 5.0, T_hot + 5.0)
    compute_density!(rho, T)
    t = 0.0; step = 0

    println("  Starting main loop...")
    t_cpu_start = time()

    while t < t_end
        piso_step!(u, v, p, T, rho, u_star, v_star, p_prime, div, S_T, T_new)
        t    += dt
        step += 1

        if step % save_interval == 0
            Nu      = compute_nusselt(T, v)
            rho_min = minimum(rho)
            rho_max = maximum(rho)
            T_min   = minimum(T)
            T_max   = maximum(T)
            u_max   = maximum(abs.(u))
            v_max   = maximum(abs.(v))
            elapsed = time() - t_cpu_start

            push!(Nu_history, Nu)
            push!(t_history, t)

            @printf("  step=%6d  t=%8.2f s  Nu=%7.4f  |u|max=%7.4f m/s  |v|max=%7.4f m/s\n",
                    step, t, Nu, u_max, v_max)
            @printf("             T=[%6.2f,%6.2f] K  ρ=[%.4f,%.4f] kg/m³  CPU=%.1f s\n",
                    T_min, T_max, rho_min, rho_max, elapsed)

            save_fields(u, v, p, T, rho, step)
        end
    end

    save_nusselt(t_history, Nu_history)

    println("\nSimulation finished.")
    @printf("  Total CPU time : %.2f s\n", time() - t_cpu_start)
    @printf("  Final Nu       : %.4f\n", Nu_history[end])
    println("  Saved: rb_lowmach_dim_step*.csv, nusselt_history.csv")
end

main()

Juliaのソースコードから説明の文章もTeXで作ってもらいました。こっちはWebのClaudeでやりましたが,これもすんなりコンパイルできるものが生成できました。

[Read More]

Volume Penalization法のテスト2

テストその2 角柱後流のカルマン渦

今回は角柱後流のカルマン渦の計算をVP法と通常の計算とで実施して,渦の放出周期が合うかやってみました。 完全に四角なので,一致するはず。

レイノルズ数は100です。

通常の計算(角柱部分のメッシュを設定)による流速分布

VP法による計算による流速分布

VP法のほうが不安定化(流れが振動しはじめて,渦が出るまで)が早いですが,最終的な渦の放出周期はほぼ一致しています。 (ここまで来るのに多少手間取ったりもしましたが。。。)

下図はスパン方向速度の時系列データです。赤が通常の計算,黒がVP法の計算によるものです。

uhist

ついでに渦度の動画

通常の計算(角柱部分のメッシュを設定)

VP法による計算

Volume Penalization法のテスト

VP法のテスト

境界埋め込み法の1つであるVP法のコードをOpenFOAMに実装しました。実装したコードはこちら。 https://qiita.com/ishigaki/items/cfb20bc58c2ae6a633a0

境界適合格子でやったときとちゃんと比べてみました。NACA0012翼周りの流れをやります。

条件

  • 流入速度44m/s
  • 迎角4度(境界速度の角度を調整して迎角を設定)
  • コード長は1m
  • Reynolds数は粘性係数で調整。Re=UinC/nu。Uは流入速度,Cはコード長,nuは動粘性係数。

メッシュ (blockMeshDict)

pisoFoam用のメッシュと,VP法つかったpisoFaomのvppisoFoam用のメッシュを作りました。blockMeshだけでほぼNACA0012通りの形状を再現できます。

メッシュ幅はおおよそ5mmです。

境界適合格子(ここをクリックするとblockMeshDictの詳細が出ます)

polyLineで翼表面の座標を直線で補間します。

/*--------------------------------*- C++ -*----------------------------------*\
| =========                 |                                                 |
| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
|  \\    /   O peration     | Version:  2.3.1                                 |
|   \\  /    A nd           | Web:      www.OpenFOAM.org                      |
|    \\/     M anipulation  |                                                 |
\*---------------------------------------------------------------------------*/
FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      blockMeshDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

convertToMeters 1;

vertices
(
(	-0.5	-1.25	-1	)
(	-0.5	1.25	-1	)
(	0.3	-0.06001727	-1	)
(	0.3	0.06001727	-1	)
(	1.0089	-1.25	-1	)
(	1.0089	0	-1	)
(	1.0089	1.25	-1	)
(	3	-1.25	-1	)
(	3	0	-1	)
(	3	1.25	-1	)
(	-0.5	-1.25	1	)
(	-0.5	1.25	1	)
(	0.3	-0.06001727	1	)
(	0.3	0.06001727	1	)
(	1.0089	-1.25	1	)
(	1.0089	0	1	)
(	1.0089	1.25	1	)
(	3	-1.25	1	)
(	3	0	1	)
(	3	1.25	1	)

);

blocks
(
    hex (0 2 3 1 10 12 13 11) (250  120  1)  simpleGrading (1 1  1)
    hex (0 4 5 2 10 14 15 12) (142  250  1)  simpleGrading (1 1 1)
    hex (3 5 6 1 13 15 16 11) (142  250  1)  simpleGrading (1 1 1)
    hex (4 7 8 5 14 17 18 15) (398  250  1)  simpleGrading (1 1 1)
    hex (5 8 9 6 15 18 19 16) (398  250  1)  simpleGrading (1 1 1)

);

edges
(
    polyLine 2 3 (
(	0.28	-0.05992581	-1	)
(	0.26	-0.059636853	-1	)
(	0.24	-0.059131171	-1	)
(	0.22	-0.058386276	-1	)
(	0.2	-0.05737543	-1	)
(	0.18	-0.056066189	-1	)
(	0.16	-0.054418208	-1	)
(	0.14	-0.052379745	-1	)
(	0.12	-0.049881816	-1	)
(	0.1	-0.046827704	-1	)
(	0.08	-0.043072299	-1	)
(	0.06	-0.03837581	-1	)
(	0.04	-0.032277225	-1	)
(	0.02	-0.023597771	-1	)
(	0.01	-0.017037074	-1	)
(	0.005	-0.012213147	-1	)
(	0	0	-1	)
(	0.005	0.012213147	-1	)
(	0.01	0.017037074	-1	)
(	0.02	0.023597771	-1	)
(	0.04	0.032277225	-1	)
(	0.06	0.03837581	-1	)
(	0.08	0.043072299	-1	)
(	0.1	0.046827704	-1	)
(	0.12	0.049881816	-1	)
(	0.14	0.052379745	-1	)
(	0.16	0.054418208	-1	)
(	0.18	0.056066189	-1	)
(	0.2	0.05737543	-1	)
(	0.22	0.058386276	-1	)
(	0.24	0.059131171	-1	)
(	0.26	0.059636853	-1	)
(	0.28	0.05992581	-1	)
        )

        polyLine 12 13 (
(	0.28	-0.05992581	1	)
(	0.26	-0.059636853	1	)
(	0.24	-0.059131171	1	)
(	0.22	-0.058386276	1	)
(	0.2	-0.05737543	1	)
(	0.18	-0.056066189	1	)
(	0.16	-0.054418208	1	)
(	0.14	-0.052379745	1	)
(	0.12	-0.049881816	1	)
(	0.1	-0.046827704	1	)
(	0.08	-0.043072299	1	)
(	0.06	-0.03837581	1	)
(	0.04	-0.032277225	1	)
(	0.02	-0.023597771	1	)
(	0.01	-0.017037074	1	)
(	0.005	-0.012213147	1	)
(	0	0	1	)
(	0.005	0.012213147	1	)
(	0.01	0.017037074	1	)
(	0.02	0.023597771	1	)
(	0.04	0.032277225	1	)
(	0.06	0.03837581	1	)
(	0.08	0.043072299	1	)
(	0.1	0.046827704	1	)
(	0.12	0.049881816	1	)
(	0.14	0.052379745	1	)
(	0.16	0.054418208	1	)
(	0.18	0.056066189	1	)
(	0.2	0.05737543	1	)
(	0.22	0.058386276	1	)
(	0.24	0.059131171	1	)
(	0.26	0.059636853	1	)
(	0.28	0.05992581	1	)
        )

    polyLine 3 5 (
(	0.32	0.05992788	-1	)
(	0.34	0.059672249	-1	)
(	0.36	0.059263278	-1	)
(	0.38	0.058712465	-1	)
(	0.4	0.058030108	-1	)
(	0.42	0.057225479	-1	)
(	0.44	0.056306948	-1	)
(	0.46	0.055282094	-1	)
(	0.48	0.054157786	-1	)
(	0.5	0.052940252	-1	)
(	0.52	0.051635135	-1	)
(	0.54	0.050247543	-1	)
(	0.56	0.048782083	-1	)
(	0.58	0.047242897	-1	)
(	0.6	0.045633691	-1	)
(	0.62	0.043957754	-1	)
(	0.64	0.042217983	-1	)
(	0.66	0.040416898	-1	)
(	0.68	0.038556656	-1	)
(	0.7	0.036639067	-1	)
(	0.72	0.034665604	-1	)
(	0.74	0.032637411	-1	)
(	0.76	0.030555316	-1	)
(	0.78	0.028419835	-1	)
(	0.8	0.02623118	-1	)
(	0.82	0.023989265	-1	)
(	0.84	0.021693713	-1	)
(	0.86	0.019343859	-1	)
(	0.88	0.016938755	-1	)
(	0.9	0.014477173	-1	)
(	0.92	0.011957609	-1	)
(	0.94	0.009378289	-1	)
(	0.96	0.006737166	-1	)
(	0.98	0.004031929	-1	)
(	1	0.00126	-1	)
        )

    polyLine 2 5 (
(	0.32	-0.05992788	-1	)
(	0.34	-0.059672249	-1	)
(	0.36	-0.059263278	-1	)
(	0.38	-0.058712465	-1	)
(	0.4  	-0.058030108	-1	)
(	0.42	-0.057225479	-1	)
(	0.44	-0.056306948	-1	)
(	0.46	-0.055282094	-1	)
(	0.48	-0.054157786	-1	)
(	0.5	    -0.052940252	-1	)
(	0.52	-0.051635135	-1	)
(	0.54	-0.050247543	-1	)
(	0.56	-0.048782083	-1	)
(	0.58	-0.047242897	-1	)
(	0.6	    -0.045633691	-1	)
(	0.62	-0.043957754	-1	)
(	0.64	-0.042217983	-1	)
(	0.66	-0.040416898	-1	)
(	0.68	-0.038556656	-1	)
(	0.7	    -0.036639067	-1	)
(	0.72	-0.034665604	-1	)
(	0.74	-0.032637411	-1	)
(	0.76	-0.030555316	-1	)
(	0.78	-0.028419835	-1	)
(	0.8	    -0.02623118	-1	)
(	0.82	-0.023989265	-1	)
(	0.84	-0.021693713	-1	)
(	0.86	-0.019343859	-1	)
(	0.88	-0.016938755	-1	)
(	0.9	    -0.014477173	-1	)
(	0.92	-0.011957609	-1	)
(	0.94	-0.009378289	-1	)
(	0.96	-0.006737166	-1	)
(	0.98	-0.004031929	-1	)
(	1	    -0.00126	-1	)
        )

    polyLine 13 15 (
(	0.32	0.05992788	1	)
(	0.34	0.059672249	1	)
(	0.36	0.059263278	1	)
(	0.38	0.058712465	1	)
(	0.4	0.058030108	1	)
(	0.42	0.057225479	1	)
(	0.44	0.056306948	1	)
(	0.46	0.055282094	1	)
(	0.48	0.054157786	1	)
(	0.5	0.052940252	1	)
(	0.52	0.051635135	1	)
(	0.54	0.050247543	1	)
(	0.56	0.048782083	1	)
(	0.58	0.047242897	1	)
(	0.6	0.045633691	1	)
(	0.62	0.043957754	1	)
(	0.64	0.042217983	1	)
(	0.66	0.040416898	1	)
(	0.68	0.038556656	1	)
(	0.7	0.036639067	1	)
(	0.72	0.034665604	1	)
(	0.74	0.032637411	1	)
(	0.76	0.030555316	1	)
(	0.78	0.028419835	1	)
(	0.8	0.02623118	1	)
(	0.82	0.023989265	1	)
(	0.84	0.021693713	1	)
(	0.86	0.019343859	1	)
(	0.88	0.016938755	1	)
(	0.9	0.014477173	1	)
(	0.92	0.011957609	1	)
(	0.94	0.009378289	1	)
(	0.96	0.006737166	1	)
(	0.98	0.004031929	1	)
(	1	0.00126	1	)
        )

    polyLine 12 15 (
(	0.32	-0.05992788	1	)
(	0.34	-0.059672249	1	)
(	0.36	-0.059263278	1	)
(	0.38	-0.058712465	1	)
(	0.4  	-0.058030108	1	)
(	0.42	-0.057225479	1	)
(	0.44	-0.056306948	1	)
(	0.46	-0.055282094	1	)
(	0.48	-0.054157786	1	)
(	0.5	    -0.052940252	1	)
(	0.52	-0.051635135	1	)
(	0.54	-0.050247543	1	)
(	0.56	-0.048782083	1	)
(	0.58	-0.047242897	1	)
(	0.6	    -0.045633691	1	)
(	0.62	-0.043957754	1	)
(	0.64	-0.042217983	1	)
(	0.66	-0.040416898	1	)
(	0.68	-0.038556656	1	)
(	0.7	    -0.036639067	1	)
(	0.72	-0.034665604	1	)
(	0.74	-0.032637411	1	)
(	0.76	-0.030555316	1	)
(	0.78	-0.028419835	1	)
(	0.8	    -0.02623118	1	)
(	0.82	-0.023989265	1	)
(	0.84	-0.021693713	1	)
(	0.86	-0.019343859	1	)
(	0.88	-0.016938755	1	)
(	0.9	    -0.014477173	1	)
(	0.92	-0.011957609	1	)
(	0.94	-0.009378289	1	)
(	0.96	-0.006737166	1	)
(	0.98	-0.004031929	1	)
(	1	    -0.00126	1	)
        )    
);

boundary
(
    inlet
    {
        type patch;
        faces
        (
            (0 1 11 10)
        );
    }
    outlet
    {
        type patch;
        faces
        (
            (0 4 14 10)
            ( 4 7 17 14)
            (1 6 16 11)
            (6 9 19 16)
            (7 8 18 17)
            (8 9 19 18) 

        );
    }
    airfoil
    {
        type wall;
        faces
        (
            (2 5 15 12)
            (2 3 13 12)
            (3 5 15 13)
            );
    }
    front
    {
        type empty;
        
        faces
        (
            (0 2 3 1)
            (0 4 5 2)
            (3 5 6 1)
            (4 7 8 5)
            (5 8 9 6)

            (10 12 13 11)
            (10 14 15 12)
            (13 15 16 11)
            (14 17 18 15)
            (15 18 19 16)

        );
    }

);

mergePatchPairs
(
);

// ************************************************************************* //
VP法用のメッシュ (ここをクリックするとblockMeshDictの詳細が出ます)

上とほぼ同じ格子幅でメッシュ生成します。

[Read More]

凝縮モデル

格納容器熱水力での凝縮モデル

シビアアクシデント時における格納容器内熱水力挙動の解析に用いられる凝縮モデルにはおおよそ2種類あります。1つは実験相関式で凝縮量を与えるモデルで,もう1つは非凝縮ガス中における水蒸気の物質拡散量から凝縮量を求めるモデルです。

2つ目の物質拡散量から凝縮量を求めるモデルを用いて,解析を行っています。そのモデルの解釈についてメモ書きを作成しました。 論文にするほどでもないので,ここで公開します。

メモ書き:Volumetric Source Term モデルの解釈について

論文の調べ方

これはなにか

卒研配属された学生さんが,先生や先輩から「論文探してみて」と言われたときにどうすればいいか,悩んだときようのアドバイスとして書いています。

そもそも論文をなぜ調べるのか

何かしら問題や困ってること,わからないことがあって,それに対して,研究することで,問題解決や新しい知見を発見するというのが一般的な流れです。研究する上で,世間ではどこまで分かってるのかを明らかにしておかないと,二度手間になる可能性があります。勉強している段階だと,同じことをやってみるというのも大事ですが,何が分かってて,何が分かっていないのかを知っておくのは大事なことです。

そのためにやるのが先行研究の調査(論文検索)です。

具体的な方法

いまどき,論文検索(サーベイ)はネットで検索するところから始まります。「論文 調べ方」を検索しても情報が多すぎてよくわからないので,とりあえずの方法を書きます。

まず調べたいキーワードがあったら,それをGoogleなりで検索すると思いますが,学術論文に関してはGoogle Scholar https://scholar.google.com/ で調べるほうが,まともな情報に当たると思います。試しに"LOCA nuclear"とか調べてみると,普通にGoogleで検索するのとだいぶ違うなという印象。

福井大学でも使えるWeb of Science https://www.flib.u-fukui.ac.jp/wos/wos.html も論文検索するのに便利です。論文がどれだけまともかとも調べられます。Sciece Direct https://www.sciencedirect.com はElsevier系の論文に関しては調べやすいので,結構使います。

論文の読み方

論文を調べていて,いきなり頭から全部読もうとしても挫折します。まずはアブストラクトを見て,自分の知りたいことが書いてあるかを考えます。その後,イントロとサマリーを見ます。それで,全部読もうと思ったら,最初からちゃんと読めばいいかと思います。まずはアブストラクトを読むことをおすすめします。

それでも最初はよくわからないことだらけだと思います。そもそも引用文献も見ないと,全体を理解するのは難しいので,引用文献もたどることになります。

さらに調べる

上でも書きましたが,論文は引用文献を含めて,相互に関連しています。Aさんのこの論文で,こういう問題提起や新しい手法が提案されて,それをBさんがさらに調査したとか改良したとかが延々連なることもあります。

なので,最初に読むべき論文を決めて,それを読んだあとは,それをさらに引用している論文や,逆に読んだ論文内で引用した論文等を読んで,調べる範囲を広げていくといいかと思います。芋づる式にたどるほうが,脈絡があるので,わかりやすい気がします。

補足(または個人的意見)

  • 機械学習とかの情報はQiita https://qiita.com/ とかにも情報がある。たまに論文紹介してる記事もある。
  • プログラムとか計算機関係のことなら,Stack overflowとか調べるとわかるかも。
  • はじめは辛いがTwitterで研究者をちまちまフォローしていくと,論文の情報とか見れるようになる。ちゃんとした研究者はTwitterで情報発信してるイメージがある。

インパクトファクター

論文の雑誌にはいろいろレベルがあります。インパクトファクターが高いと,有名雑誌というやつで,NatureとかScienceは40くらいあります。原子力系だとそこまでインパクトファクター高い雑誌がないので,インパクトファクターが高くないから信頼できないかという一概にそういうわけでもないです。Nucl. Eng. Des.とはインパクトファクター1ちょいですが,原子力業界の人間なら誰でも見てます。

熱音響実験

熱音響自励振動実験

去年から始めたループ管熱音響エンジンの実験ですが,ついに自励振動するようになりました!めでたい。

現状のループはこんな感じ。

engine_photo

下記はデータロガーの画面ですが,途中から振幅が増幅しているのが圧力の計測データです。振動が開始して,振幅が増大しています。

engine_graph

圧力波形をFFTかけたデータ。130Hzにピークがあり,ループ1周を1波長とするモードで発振しています。

engine_FFT

興味のある方は見学にいらしてください。

今年の会議2

国際会議など

今年は新型コロナ(COVID-19)の影響で,ほとんどの会議が中止かオンライン開催に移行しています。原子力学会はオンライン開催でした。

「日韓の熱流動の会議NTHASは当初の開催予定を変更して,2021年3月開催予定になりました。」と9月に書いたのですが,結局更に延期になって,2022年開催になりました。 https://www.nthas12.org

数値流体力学シンポも沖縄開催予定が結局オンラインのみになりました。沖縄に行けなかったのは残念です。 https://www2.nagare.or.jp/cfd/cfd34/index.html

反面,オンラインで開催のイベントが増えて,東京とか出張するのはためらわれたイベントも参加しやくなったりしました。今後もオンラインとハイブリッドでイベントをやる文化は残ってほしいです。

熱音響エンジンの製作2

熱音響自励振動実験

去年から始めたループ管熱音響エンジンの実験ですが,なかなかうまくいかないです。 今日もいろいろやりましたが,自励発振に至らず。

熱音響現象の例で出てくる。Rijke管。オランダ人の同僚の先生に聞いたら,発音は「レイケ」じゃないみたいです。「ライク」だか「ライケ」みたいです。

engine2 engine3

今年の会議

国際会議など

今年は新型コロナ(COVID-19)の影響で,ほとんどの会議が中止かオンライン開催に移行しています。原子力学会はオンライン開催でした。

NUTHOSは中止になり,代わりにATHという会議で代替することになりましたが,こちらもどういう感じで開催されるのかはっきりしません。

日韓の熱流動の会議NTHASは当初の開催予定を変更して,2021年3月開催予定になりました。 https://www.nthas12.org

これは今のところ,開催されそうですが,これくらいしか現地開催の会議なさそうです。

数値流体力学シンポは沖縄開催なので,申し込みましたが,オンラインだけになる可能性もあります。沖縄いけるといいなー。 https://www2.nagare.or.jp/cfd/cfd34/index.html

熱音響エンジンの製作1

熱音響エンジン製作開始

今年度はまず熱音響現象の研究の手始めに熱音響エンジンの製作に着手しました。物の手配が大体済んだので,今日から製作開始です。

まずは配管切るところから。蓄熱器も業者さんに作ってもらいました。

engine1

普段HIOKIのデータロガー使ってますが,今回初めてグラフテックのロガーを使ってみました。使い勝手は異なる部分も多いですが,グラフテックもなかなか使えると思います。