こんにちは(@t_kun_kamakiri)

ベナール対流は、液体の底面を温めたときに自然に生まれる「六角形のセル模様」が特徴的な対流現象です。


下から加熱されて軽くなった流体が上昇し、冷えて重くなった流体が下降することで、規則的な渦のパターンが自発的に形成されます。

外から“回せ”と指示しなくても、温度差だけで秩序ある流れが生まれる点が非常に興味深い現象です。

本記事では、この ベナール対流を OpenFOAM を用いて数値的に再現し、温度分布やセル構造がどのように形成されるのかを確認していきます。

本記事の内容

OpenFOAMのチュートリアルにあるベナール対流を計算してみよう


熱対流の基礎を学ぶ上でも、自然対流モデルの検証ケースとしても、非常に分かりやすい題材です。

また、チュートリアルを実行するだけですのでOpenFOAMをインストールされている方であれば、すぐに試すことができます。

こういった可視化すると楽しい現象を見つけて、OpenFOAMを学ぶのもモチベーションを保つコツです。

OpenFOAM v2412(WSL2 Ubuntu-24.04)

チュートリアルのコピー

チュートリアルをコピーします。

cp -r $FOAM_TUTORIALS/heatTransfer/buoyantBoussinesqPimpleFoam/BenardCells .

$FOAM_TUTORIALS = /usr/lib/openfoam/openfoam2412/tutorials
という変数ですので、ご自身使用している環境に合ったバージョンがはいります。

続いてフォルダを移動します。

cd BenardCells

メッシュ作成

今回は2次元の解析になりますので$z$軸方向は1層のメッシュです。

blockMeshDictを確認します。

scale   1;

vertices
(
    (0 0 -1)
    (9 0 -1)
    (9 1 -1)
    (0 1 -1)
    (0 0 1)
    (9 0 1)
    (9 1 1)
    (0 1 1)
);

blocks
(
    hex (0 1 2 3 4 5 6 7) (90 10 1) simpleGrading (1 1 1)
);

edges
(
);

boundary
(
    floor
    {
        type wall;
        faces
        (
            (1 5 4 0)
        );
    }

    ceiling
    {
        type wall;
        faces
        (
            (3 7 6 2)
        );
    }

    sideWalls
    {
        type wall;
        faces
        (
            (0 4 7 3)
            (2 6 5 1)
        );
    }

    frontAndBack
    {
        type empty;
        faces
        (
            (0 3 2 1)
            (4 5 6 7)
        );
    }
);

mergePatchPairs
(
);

以下のコマンドでメッシュを作成します。

blockMesh

ParaViewで確認するためには空ファイルを用意します。

touch post.foam

blockMeshDictで設定した境界面の名前が作成されています。

境界条件

チュートリアルのままですが境界条件を載せておきます。

0/U

dimensions      [0 1 -1 0 0 0 0];

internalField   uniform (1e-4 0 0);

boundaryField
{
    wall
    {
        type            noSlip;
    }

    #includeEtc "caseDicts/setConstraintTypes"
}

0/T

dimensions      [0 0 0 1 0 0 0];

internalField   uniform 300;

boundaryField
{
    floor
    {
        type            fixedValue;
        value           uniform 301;
    }

    ceiling
    {
        type            fixedValue;
        value           uniform 300;
    }

    sideWalls
    {
        type            zeroGradient;
    }

    #includeEtc "caseDicts/setConstraintTypes"
}

0/p_rgh

dimensions      [0 2 -2 0 0 0 0];

internalField   uniform 0;

boundaryField
{
    wall
    {
        type            fixedFluxPressure;
        rho             rhok;
        value           $internalField;
    }

    #includeEtc "caseDicts/setConstraintTypes"
}

0/p

dimensions      [0 2 -2 0 0 0 0];

internalField   uniform 0;

boundaryField
{
    wall
    {
        type            calculated;
        value           $internalField;
    }

    #includeEtc "caseDicts/setConstraintTypes"
}

0/k

dimensions      [0 2 -2 0 0 0 0];

internalField   uniform 1e-5;

boundaryField
{
    wall
    {
        type            kqRWallFunction;
        value           $internalField;
    }

    #includeEtc "caseDicts/setConstraintTypes"
}

0/epsilon

dimensions      [0 2 -3 0 0 0 0];

internalField   uniform 1e-5;

boundaryField
{
    wall
    {
        type            epsilonWallFunction;
        value           $internalField;
    }

    #includeEtc "caseDicts/setConstraintTypes"
}

0/alphat

dimensions      [0 2 -1 0 0 0 0];

internalField   uniform 0;

boundaryField
{
    wall
    {
        type            alphatJayatillekeWallFunction;
        Prt             0.85;
        value           $internalField;
    }

    #includeEtc "caseDicts/setConstraintTypes"
}

離散化スキームと代数ソルバ

離散化スキームはいろいろありますが、余裕があればこちらもお読みください。

【OpenFOAM】移流方程式で色々な離散化スキーム(解の振る舞いと計算時間) こんにちは(@t_kun_kamakiri) 前回の記事では移流方程式における移流項の離散化スキームを変えて結果がどう変わ...

system/fvSchemes

ddtSchemes
{
    default         Euler;
}

gradSchemes
{
    default         Gauss linear;
}

divSchemes
{
    default         none;

    div(phi,U)      Gauss linearUpwind grad(U);
    div(phi,T)      Gauss limitedLinear 1;

    turbulence      Gauss limitedLinear 1;
    div(phi,k)      $turbulence;
    div(phi,epsilon) $turbulence;

    div((nuEff*dev2(T(grad(U))))) Gauss linear;
}

laplacianSchemes
{
    default         Gauss linear corrected;
}

interpolationSchemes
{
    default         linear;
}

snGradSchemes
{
    default         corrected;
}

代数ソルバはこちらをごさんこうください。

system/fvSolution

solvers
{
    p_rgh
    {
        solver          GAMG;
        smoother        DIC;
        tolerance       1e-8;
        relTol          0.01;
    }

    p_rghFinal
    {
        $p_rgh;
        relTol          0;
    }

    "(U|T|k|epsilon)"
    {
        solver          PBiCGStab;
        preconditioner  DILU;
        tolerance       1e-8;
        relTol          0.01;
    }

    "(U|T|k|epsilon)Final"
    {
        $T;
        relTol          0;
    }
}

PIMPLE
{
    momentumPredictor no;
    nNonOrthogonalCorrectors 0;
    nCorrectors       2;
    pRefCell          0;
    pRefValue         0;
}

relaxationFactors
{
    equations
    {
        ".*"   1.0;
    }
}

計算制御の設定

system/controlDict

application     buoyantBoussinesqPimpleFoam;

startFrom       latestTime;

startTime       0;

stopAt          endTime;

endTime         1000;

deltaT          1;

writeControl    runTime;

writeInterval   50;

purgeWrite      0;

writeFormat     binary;

writePrecision  6;

writeCompression off;

timeFormat      general;

timePrecision   6;

runTimeModifiable true;

functions
{
    #includeFunc solverInfo
    #includeFunc streamlines
}

functionsの中に2つ設定ファイルがあります。

1つ目が残差を出力する設定ファイルです。
ここでは圧力(p_rgh)の残差のみを出力しています。

#includeEtc "caseDicts/postProcessing/numerical/solverInfo.cfg"

writeControl writeTime;

fields      (p_rgh);

writeFields yes;

2つ目が流線のvtkファイルを出力する設定ファイルです。

こちらの設定によりstartからendの直線を通過するstreamlines(流線)を作ります。

nLines  24;
start   (0 0.5 0);
end     (9 0.5 0);
fields  (p);

#includeEtc "caseDicts/postProcessing/visualization/streamlines.cfg"

このように必要に応じて出力する設定ファイルを各自用意することになります。

設定ファイルはネットから調べてくる必要がありますが、とりあえずはチュートリアルのままで良いでしょう。

では、計算を実行します。

buoyantBoussinesqPimpleFoam

計算は1分くらいで終わると思います。

結果の確認

postProcessing/sets/streamlines/1000/track0.vtp
をParaViewで読み込むと

まとめ