next up previous
Next: 1.3 入手してコンパイル・実行 Up: 1 まずサンプル・プログラムを試そう Previous: 1.1 一体何をしているのか?

1.2 サンプル・プログラム heat1d-i-glsc.c を読む


   1 /*
   2  * heat1d-i-glsc.c -- 陰的スキームで熱方程式を解く
   3  *
   4  *   菊地・山本, 微分方程式と計算機演習, 山海堂 (1991) 第11章
   5  *   p237 のプログラムを修正・拡張したもの
   6  *
   7  C     ** IMPLICIT FINITE DIFFERENCE METHOD **
   8  C     **         FOR HEAT EQUATION         **
   9  C     nfunc : 関数の番号(5種類の関数を用意している)
  10  C     theta : スキームのパラメーター
  11  C     N : 分割の数
  12  C     t : 時刻
  13  C     tau : 時間の刻み幅
  14  C     Tmax : 時刻の上限
  15  C     h : 空間の刻み幅
  16  C     AL,AD,AU : 係数行列
  17  C     u : DIMENSION FOR UNKNOWN FUNCTION
  18  *
  19  *  このプログラムは
  20  *    http://www.math.meiji.ac.jp/%7Emk/program/
  21  *  から入手可能
  22  */
  23 
  24 #include <stdio.h>
  25 #include <stdlib.h>
  26 #include <math.h>
  27 
  28 #define G_DOUBLE
  29 #include <glsc.h>
  30 
  31 /* 円周率 --- main() で pi = 4.0 * atan(1.0); と代入 */
  32 
  33 double pi;
  34 
  35 void trilu(int, double *, double *, double *);
  36 void trisol(int, double *, double *, double *, double *);
  37 double f(double, int);
  38 void print100(int, double, double *);
  39 void axis(double, double, double, double);
  40 void fsymbol(double, double, char *);
  41 void print_parameters(double, double, double, double,
  42                       int, double, int,
  43                       double, double, double, double);
  44 void save_picture(void);
  45 
  46 int main()
  47 {
  48     int N, nfunc, i, n, nMax;
  49     double a, b, theta, h, Tmax, tau, c1, c2, c3, c4, lambda, t;
  50     double *AL, *AD, *AU, *ff, *u;
  51     double xleft, ybottom, xright, ytop;
  52     /* グラフを書き換える時間間隔 */
  53     double dt;
  54     /* 何ステップおきにグラフを書き換えるか */
  55     int skip;
  56     /* 以前描いたグラフを消すか? (0=No, 1=Yes) */
  57     int erase_always = 0;
  58     /* 数値を表示するか? (0=No, 1=Yes) */
  59     int print_numerical_data = 0;
  60     /* */
  61     double win_width, win_height, w_margin, h_margin;
  62 
  63     /* 円周率 */
  64     pi = 4.0 * atan(1.0);
  65     /* 区間の左端、右端 */
  66     a = 0.0;
  67     b = 1.0;
  68 
  69     /* 入力 */
  70     printf("入力して下さい : nfunc(1..5)=");
  71     scanf("%d", &nfunc);
  72     printf("入力して下さい : θ=");
  73     scanf("%lf", &theta);
  74     printf("入力して下さい : N=");
  75     scanf("%d", &N);
  76     printf("入力して下さい : λ=");
  77     scanf("%lf", &lambda);
  78 
  79     /* ベクトルの準備 */
  80     if ((AL = malloc(N * sizeof(double))) == NULL) {
  81         fprintf(stderr, "cannot allocate AL[]\n");
  82         exit(1);
  83     }
  84     if ((AD = malloc(N * sizeof(double))) == NULL) {
  85         fprintf(stderr, "cannot allocate AD[]\n");
  86         exit(1);
  87     }
  88     if ((AU = malloc(N * sizeof(double))) == NULL) {
  89         fprintf(stderr, "cannot allocate AU[]\n");
  90         exit(1);
  91     }
  92     if ((ff = malloc(N * sizeof(double))) == NULL) {
  93         fprintf(stderr, "cannot allocate ff[]\n");
  94         exit(1);
  95     }
  96     if ((u = malloc((N + 1) * sizeof(double))) == NULL) {
  97         fprintf(stderr, "cannot allocate u[]\n");
  98         exit(1);
  99     }
 100     /* */
 101     h = (b - a) / N;
 102     tau = lambda * h * h;
 103     printf(" 時間の刻み幅τ= %g になりました。\n", tau);
 104 
 105     /* 入力 */
 106     printf("入力して下さい : 最終時刻 Tmax=");
 107     scanf("%lf", &Tmax);
 108     printf("入力して下さい : グラフ書き換え時間間隔 Δt=");
 109     scanf("%lf", &dt);
 110     skip = (int) rint(dt / tau);
 111     /* skip が 0 になっていないかチェック */
 112     if (skip == 0) {
 113         fprintf(stderr, "Δtが小さすぎるので、Δt=τ=%g にします。\n", tau);
 114         skip = 1;
 115         dt = tau;
 116     }
 117 
 118     /* 係数行列の準備 */
 119     c1 = - theta * lambda;
 120     c2 = 1.0 + 2.0 * theta * lambda;
 121     c3 = 1.0 - 2.0 * (1.0 - theta) * lambda;
 122     c4 = (1.0 - theta) * lambda;
 123     for (i = 1; i < N; i++) {
 124         AL[i] = c1;
 125         AD[i] = c2;
 126         AU[i] = c1;
 127     }
 128 
 129     /* 初期値 */
 130     for (i = 0; i <= N; i++)
 131         u[i] = f(a + i * h, nfunc);
 132 
 133     /* 出力(t=0) */
 134     /* 出力(ここでは画面に数値を表示すること)は FORTRAN と C で
 135        かなり異なるので、直接の翻訳は出来ない。ごたごたするので、
 136        print100 という関数にまとめた */
 137     t = 0.0;
 138     print100(N, t, u);
 139 
 140     /* ***************** グラフィックスの準備 ***************** */
 141     /* グラフィックス画面の準備 -- まず画面の範囲を決めてから */
 142     xleft = a - (b - a) / 10;
 143     xright = b + (b - a) / 10;
 144     ybottom = - 0.1;
 145     ytop = 1.1;
 146     /* メタファイル名は "HEAT",
 147      * ウィンドウのサイズは、
 148       横 win_width  + 2 * w_margin, 縦 win_height + 2 * h_margin */
 149     win_width = 200.0; win_height = 200.0; w_margin = 10.0; h_margin = 10.0;
 150     g_init("HEAT", win_width  + 2 * w_margin, win_height + 2 * h_margin);
 151     /* 画面とメタファイルの両方に記録する */
 152     g_device(G_BOTH);
 153     /* 座標系の定義: [-0.1,1.1]×[-0.1,1.1] という閉領域を表示する */
 154     g_def_scale(0,
 155                -0.1, 1.1, -0.1, 1.1,
 156                 w_margin, h_margin, win_width, win_height);
 157     /* 線を二種類用意する */
 158     g_def_line(0, G_BLACK, 0, G_LINE_SOLID);
 159     g_def_line(1, G_BLACK, 0, G_LINE_DOTS);
 160     /* 表示するための文字列の属性を定義する */
 161     g_def_text(0, G_BLACK, 3);
 162     /* 定義したものを選択する */
 163     g_sel_scale(0); g_sel_line(0); g_sel_text(0);
 164 
 165     /* タイトルと入力パラメーターを表示する */
 166     print_parameters(xleft, ybottom, xright, ytop,
 167                      nfunc, theta, N,
 168                      lambda, tau, Tmax, t);
 169 
 170     /* 座標軸を表示する */
 171     g_sel_line(1);
 172     g_move(-0.1, 0.0); g_plot(1.1, 0.0);
 173     g_move(0.0, -0.1); g_plot(0.0, 1.1);
 174     g_sel_line(0);
 175 
 176     /* t=0 の状態を表示する */
 177     g_move(0.0, u[0]);
 178     for (i = 1; i <= N; i++)
 179       g_plot(i * h, u[i]);
 180 
 181     /* 係数行列の LU 分解 */
 182     trilu(N - 1, AL + 1, AD + 1, AU + 1);
 183 
 184     /* 繰り返し計算 */
 185     nMax = (int) rint(Tmax / tau);
 186     for (n = 1; n <= nMax; n++) {
 187         /* 連立1次方程式を作って解く */
 188         for (i = 1; i < N; i++)
 189             ff[i] = c3 * u[i] + c4 * (u[i - 1] + u[i + 1]);
 190         trisol(N - 1, AL + 1, AD + 1, AU + 1, ff + 1);
 191         for (i = 1; i < N; i++)
 192             u[i] = ff[i];
 193         /* 境界条件 */
 194         u[0] = 0.0;
 195         u[N] = 0.0;
 196 
 197         /* 時刻 */
 198         t = n * tau;
 199 
 200         if (n % skip == 0) {
 201             /* 数値データの表示 */
 202             if (print_numerical_data)
 203                 print100(N, t, u);
 204 
 205             /* 解のグラフを描く */
 206             if (erase_always) {
 207                 /* これまで描いたものを消し、パラメーターを再表示する */
 208                 g_cls();
 209                 print_parameters(xleft, ybottom, xright, ytop,
 210                                  nfunc, theta, N, lambda, tau, Tmax, t);
 211             }
 212             g_move(0.0, u[0]);
 213             for (i = 1; i <= N; i++)
 214               g_plot(i * h, u[i]);
 215         }
 216     }
 217 
 218     printf("マウスでウィンドウをクリックして下さい。\n");
 219     g_sleep(-1.0);
 220     g_term();
 221 
 222     return 0;
 223 }
 224 
 225 /**********************************************************/
 226 
 227 /* 三重対角行列の LU 分解 (pivoting なし) */
 228 void trilu(int n, double *al, double *ad, double *au)
 229 {
 230   int i, nm1 = n - 1;
 231   /* 前進消去 (forward elimination) */
 232   for (i = 0; i < nm1; i++) {
 233     al[i + 1] /= ad[i];
 234     ad[i + 1] -= au[i] * al[i + 1];
 235   }
 236 }
 237 
 238 /* LU 分解済みの三重対角行列を係数に持つ三項方程式を解く */
 239 void trisol(int n, double *al, double *ad, double *au, double *b)
 240 {
 241   int i, nm1 = n - 1;
 242   /* 前進消去 (forward elimination) */
 243   for (i = 0; i < nm1; i++) b[i + 1] -= b[i] * al[i + 1];
 244   /* 後退代入 (backward substitution) */
 245   b[nm1] /= ad[nm1];
 246   for (i = n - 2; i >= 0; i--) b[i] = (b[i] - au[i] * b[i + 1]) / ad[i];
 247 }
 248 
 249 /**********************************************************/
 250 
 251 /* 初期条件を与える関数。複数登録して番号 nfunc で選択する。 */
 252 
 253 double f(double x, int nfunc)
 254 {
 255     /* f(x)=max(x,1-x) */
 256     if (nfunc == 1) {
 257         if (x <= 0.5)
 258             return x;
 259         else
 260             return 1.0 - x;
 261     }
 262     /* f(x)=1 */
 263     else if (nfunc == 2)
 264         return 1.0;
 265     else if (nfunc == 3)
 266         return sin(pi * x);
 267     /* f(x)= 変な形をしたもの(by M.Sakaue) */
 268     else if (nfunc == 4) {
 269         if (x <= 0.1)
 270             return 5 * x;
 271         else if (x <= 0.3)
 272             return -2 * x + 0.7;
 273         else if (x <= 0.5)
 274             return 4.5 * x - 1.25;
 275         else if (x <= 0.7)
 276             return -x + 1.5;
 277         else if (x <= 0.9)
 278             return x + 0.1;
 279         else
 280             return -10 * x + 10.0;
 281     }
 282     /* やはり非対称な形をしたもの(by M.Sakaue) */
 283     else if (nfunc == 5) {
 284         if (x <= 0.2)
 285             return -x + 0.8;
 286         else if (x <= 0.3)
 287             return 4 * x - 0.2;
 288         else if (x <= 0.4)
 289             return -4 * x + 2.2;
 290         else if (x <= 0.7)
 291             return -x + 1.0;
 292         else if (x <= 0.8)
 293             return 4 * x - 2.6;
 294         else
 295             return -x + 1.5;
 296     } else
 297         return 0;
 298 }
 299 
 300 /**********************************************************/
 301 
 302 /* 配列 u[] の内容(u[0],u[1],...,u[n])を一行 5 個ずつ表示する。 */
 303 
 304 void print100(int N, double t, double *u)
 305 {
 306     int i;
 307     printf("T= %12.4e\n", t);
 308     for (i = 0; i < 5; i++)
 309         printf("  I     u(i)    ");
 310     printf("\n");
 311     for (i = 0; i <= N; i++) {
 312         printf("%3d%12.4e ", i, u[i]);
 313         if (i % 5 == 4)
 314             printf("\n");
 315     }
 316     printf("\n");
 317 }
 318 
 319 /**********************************************************/
 320 
 321 /* ウィンドウに座標軸を表示する */
 322 void axis(double xleft, double ybottom, double xright, double ytop)
 323 {
 324     g_sel_line(1);
 325     g_move(xleft, 0.0); g_plot(xright, 0.0);
 326     g_move(0.0, ybottom); g_plot(0.0, ytop);
 327     g_sel_line(0);
 328 }
 329 
 330 /* パラメーターの値等をウィンドウに表示する */
 331 void print_parameters(double xleft, double ybottom, double xright, double ytop,
 332                       int nfunc, double theta, int N,
 333                       double lambda, double tau, double Tmax, double t)
 334 {
 335     char message[80];
 336 
 337     axis(xleft, ybottom, xright, ytop);
 338 
 339     g_text(30.0, 30.0, "heat equation, u(0)=u(1)=0");
 340     sprintf(message, "nfunc=%d, theta=%g, N=%d, lambda=%g, tau=%g, Tmax=%g",
 341             nfunc, theta, N, lambda, tau, Tmax);
 342     g_text(30.0, 60.0, message);
 343     if (t != 0.0) {
 344         sprintf(message, "t = %g", t);
 345         g_text(30.0, 90.0, message);
 346     }
 347 
 348 }


next up previous
Next: 1.3 入手してコンパイル・実行 Up: 1 まずサンプル・プログラムを試そう Previous: 1.1 一体何をしているのか?
Masashi Katsurada
平成18年6月6日