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 }