Part.19

2次元拡散方程式(熱伝導方程式)の数値解法

空間2次元の熱伝導方程式を考えます。1次元の問題が、棒の熱分布を考えていたのに対し、2次元の問題では、薄い板の熱分布を考えていると想像してください。

問題は、

Find u(x,y,t) s.t.

となります。これを数値計算する為に離散化を行います。一次元の場合と同様なので、途中は省略すると、

という式が得られます。まとめると、

となります。特に、dx=dy として、

とかけば、

なる安定性条件を満たす必要があるので、注意が必要です(一次元の場合よりも、条件が厳しくなっています)。

これを解く為には、2次元配列が必要です。double 型の2次元配列は次のように宣言します。

double A[100][100];

これで、100x100の配列を作成できます。配列の要素は A[1][4] といったふうに参照できます。これで行列Aの1行4列目の要素を参照できます。(もちろん大きさには制限があります。あまりに巨大なものは扱えません。)

double A[101][101];

と宣言すると、A[0][0], A[0][1], ...., A[0][99],.....,A[100][100] と101x101の配列となります。

同様に、

double u[101][101];

double new_u[101][101];

という配列を作れば、一次元の熱方程式のアルゴリズムほぼそのままで計算できます。次の例では、D=1, Lx=Ly=1 の場合の例です。ただし完成版ではなく、太字のコメント部分が抜けています。

/* 2次元熱伝導方程式のアニメーション */
/*                                     D.Ueyama */
/*                                     未完成版 */
#include <glsc.h>
#include <math.h>
#include <stdio.h>

/* 定数の設定 */
#define PI (3.1415926)

/* 空間刻数 */
#define IMAX (100)
#define JMAX (IMAX)

/* TMAX */
#define TMAX (0.05)

/* Lx, Ly */
#define Lx (1.0)
#define Ly (Lx)

/* 画面表示間隔 */
#define INTV (100)

/* 仮想座標系の下限・上限 */
#define BOT (-1.0)
#define TOP ( 1.0)

int main()
{
    /* プログラム中で使用する変数の型宣言 */
    int i, j, k;
    int KMAX;
    double u[IMAX+1][JMAX+1], new_u[IMAX+1][JMAX+1], t, x, y, dt, 
           dx, dy, ax, ay;
    char text[256];

    /* dt, dx 等を設定 */
    dx = Lx/IMAX;
    dy = Ly/JMAX;

    /* ここでは dx = dy を仮定している */
    /* ax が 1/6 となるようにしているが */
    /* 実際には dx,dy のうち小さい方から dt を決めるべきである */
    dt = (dx*dx)/6.0;
    ax = dt/(dx*dx);
    ay = dt/(dy*dy);
    KMAX = TMAX/dt;

    /* GLSCの初期設定 */
    g_init("Graph", 200.0, 200.0);
    g_device(G_DISP);

    /* 仮想座標系を一つ用意 (今回は使わない)*/
    g_def_scale(0, 0.0, Lx, BOT, TOP, 15.0, 10.0, 175.0, 80.0);

    g_cls();
    g_sel_scale(0);

    /* 初期値設定 */
    
    /* 境界条件設定 */

    /* 時間方向へのループ(アニメーション)*/
    for(k = 0; k <= KMAX; k++)
    {
        t = k*dt;

        /* INTV 間隔でグラフを表示 */
        if( k%INTV == 0 )
        {
            /* 一つ前の時間のグラフを消す */
            g_cls();

            /* テキストの表示 */
            sprintf(text, "2D Heat Equation (%d*%d) by u11****", IMAX, JMAX);
            g_text(50.0,7.0, text);

            /* t の表示 */
            sprintf(text, "t = %7.5lf", t);
            g_text(85.0,20.0, text);

            /* 配列 u をグラフとして描く (鳥瞰図)*/
            g_line_color(G_RED);
            g_line_width(1.0);
            g_hidden(100.0, 100.0, 50.0, 0.0, 1.0, 500.0, 40.0, 35.0,
                        10.0, 10.0, 180.0, 180.0, 
                        (double *)u, IMAX+1, JMAX+1, 1, 0, 5, 5);

            if( k == 0 )
            {
                /* 初期値を良くみたいので */
                /* 初期値の場合は表示しクリックを待つ */
                g_sleep(G_STOP);
            }

            /* 初期値以外の場合は 0.01 秒間止る */
            /* これによりアニメーションのちらつきをある程度おさえる */
            else
            {
                g_sleep(0.01);
            }
        }

        /* 空間方向へのループ */
        /* 時間を1ステップ進める */
        /* u から new_u を求める */
       

        /* new_u を u にコピー */
        
    }

    /* 計算終了後マウスがクリックされるまで停止 */
    g_sleep(G_STOP);
}

課題1

初期値は境界条件を満たす適当なものを与えるものとし、上記プログラムを完成させよ。

課題2

プログラム中にコメントとして書いてあるが、ここでは dx = dy を仮定して ax = 1/6 となるように dt を決定している。このままだと、 dx > dy といった状況(例えば、 IMAX=100 で JMAX=200 とした場合など)では安定性条件を破る可能性がある(安定した計算のためには ax, ay それぞれが 1/4 より小さいことが必要)。dx と dy の大小関係から ax および ay のうち大きい方が 1/6 となるように dt を決定するプログラムに変更せよ。

上記プログラムでは図を鳥瞰図で表示しています。各引数の意味は、GLSCマニュアルを参照してください。適当に見る角度等は変えてもらって結構です。


g_hidden(100.0, 100.0, 50.0, 0.0, 1.0, 500.0, 40.0, 35.0, 10.0, 10.0, 180.0, 180.0,

(double *)u, IMAX+1, IMAX+1, 1, 0, 5, 5);

ただし、上記例は

g_init("Graph", 200.0, 200.0);

にて開いたウィンドウを仮定しています。

上の例にある g_hidden の引数中に (double *)u なるものがありますが、単に u と書いても正常に動きます。ただし、その場合コンパイル時に警告がでます。この (float *) なる記述はキャストと呼ばれるもので、変数の型をかえる場合に使います。今回の場合、 g_hidden 関数の第13引数は float * 型を要求するのですが、ここでの u は2次元配列であるため、 float ** 型となり、キャストが必要となります。

2次元熱方程式のアニメーションサンプルを用意しました。このサンプルでは、初期値として、

sin(3*PI*x)*sin(2*PI*y)

を用いています。