/******************************************************************************
** [laplace.c]
** ラプラス方程式を差分法により解きます。
**
** 使い方: laplace.exe > result.txt
******************************************************************************/
#include <stdio.h>
#include <math.h>
/*
** 計算領域の分割数 NX, NY を決めます。理論上は分割数を細かくすれば
** するほど計算誤差は減るはずですが、コンピュータが扱う浮動小数点数
** は有限値なので、あまり細かくしすぎると、桁落ちによって逆に
** 計算精度が落ちると思います。いろいろな DX, DY で結果を比較して、
** 計算精度が充分に良いところで、かつ計算速度も妥当な分割数を選ぶと
** 良いと思います。ここでは仮に 41 x 41 とします。
** NX と NY は同じ数にした方が、微分方程式の特性に合うらしいです。
*/
#define NX 41
#define NY 41
/*
** for (y のループ) { for (x のループ) { (x 方向の計算); } }
** とする場合は、C言語の配列のメモリ配置を考慮して、T(x, y)
** ではなく、T(y, x) とします。
*/
double T[NY][NX];
/*-----------------------------------------------------------------------------
** [SetBoundaryCondition]
** 境界条件を設定します。
** 2次元配列の扱い方はいろいろあります。書籍や web page 等を
** あたってみてください。
**---------------------------------------------------------------------------*/
void SetBoundaryCondition(double T[NY][NX])
{
int x;
int y;
double left, top, right, bottom;
fprintf(stderr, "left top right bottom\n");
scanf_s("%lf%lf%lf%lf", &left, &top, &right, &bottom);
/* 領域の底の境界値 */
for (x = 0; x < NX; x++)
{
T[0][x] = bottom;
}
/* 領域の上の境界値 */
for (x = 0; x < NX; x++)
{
T[NY-1][x] = top;
}
/* 左の壁の境界値 */
for (y = 0; y < NY; y++)
{
T[y][0] = left;
}
/* 右の壁の境界値 */
for (y = 0; y < NY; y++)
{
T[y][NX-1] = right;
}
}
int main(void)
{
const double dx = 1.0 / (NX-1);
const double dy = 1.0 / (NY-1);
/* 式(16)から、C1 と C2 の値を先に計算しておきます。 */
const double C1 = 0.5 * dx*dx / (dx*dx + dy*dy);
const double C2 = 0.5 * dy*dy / (dx*dx + dy*dy);
int i;
int j;
double errorMax;
double previousValue;
/*
** 境界条件を設定します。この境界条件は
** ディリクレ条件なので、はじめに1度だけ設定
** すればOKです。
*/
SetBoundaryCondition(T);
/*
** 前回の計算値と今回の計算値との最大誤差が
** 0.00001 を下回るまで繰り返します。
*/
do
{
errorMax = 0.0;
/* 計算領域は境界の1つ内側です。 */
for (i = 1; i < NY-1; i++)
{
for (j = 1; j < NX-1; j++)
{
previousValue = T[i][j];
/* ガウス・ザイデル法 */
T[i][j] = C1*(T[i+1][j] + T[i-1][j]) + C2*(T[i][j+1] + T[i][j-1]);
if (errorMax < fabs(T[i][j] - previousValue))
{
errorMax = fabs(T[i][j] - previousValue);
}
}
}
} while (errorMax > 0.00001);
/* 全領域の計算結果を出力します。 */
for (i = 0; i < NY; i++)
{
for (j = 0; j < NX; j++)
{
printf("%d\t%d\t%15.15f\n", j, i, T[i][j]);
}
printf("\n");
}
return 0;
}
// gnuplot>cd 'C:\hoge'
// gnuplot>set pm3d
// gnuplot>splot 'result.txt'