Part.19
2次元拡散方程式(熱伝導方程式)の数値解法
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);}初期値は境界条件を満たす適当なものを与えるものとし、上記プログラムを完成させよ。
プログラム中にコメントとして書いてあるが、ここでは 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)
を用いています。