「長方形領域における熱方程式に対する差分法 -- MATLAB を使って数値計算」
「2次元熱方程式の非同次 Dirichlet 境界値問題を解く差分法プログラムを作る」 という解説を書いてる。
以下は、そこで説明した非同次Dirichlet境界値問題のプログラムである。
| heat2d_nh.m |
% heat2d_nh.m
% 長方形領域における熱方程式 u_t=△u (非同次Dirichlet境界条件) を差分法で解く
% 入手 curl -O https://m-katsurada.sakura.ne.jp/program/fdm/heat2d_nh.m
% 解説 https://m-katsurada.sakura.ne.jp/labo/text/heat-nonhomo.pdf
% 一応の完成をして、heat2d_nh.m として公開した (2024/8/17)。少し推敲 (2024/8/24)。
function heat2d_nh
% Dirichlet境界条件の場合の係数行列
function [A,B]=heat2d_mat(Nx,Ny,lambdax,lambday,theta)
Ix=speye(Nx-1,Nx-1);
Iy=speye(Ny-1,Ny-1);
vx=ones(Nx-2,1);
Jx=sparse(diag(vx,1)+diag(vx,-1));
vy=ones(Ny-2,1);
Jy=sparse(diag(vy,1)+diag(vy,-1));
Kx=2*Ix-Jx;
Ky=2*Iy-Jy;
% row major (y changes first, l=j+(Ny-1)*i)
A=kron(Ix,Iy)+theta*lambday*kron(Ix,Ky)+theta*lambdax*kron(Kx,Iy);
B=kron(Ix,Iy)-(1-theta)*lambday*kron(Ix,Ky)-(1-theta)*lambdax*kron(Kx,Iy);
% column major (x chandes first, l=i+(Nx-1)*j)
% A=kron(Iy,Ix)+theta*lambdax*kron(Iy,Kx)+theta*lambday*kron(Ky,Ix);
% B=kron(Iy,Ix)-(1-theta)*lambdax*kron(Iy,Kx)-(1-theta)*lambday*kron(Ky,Ix);
end
% 長方形領域 (a,b)×(c,d)
a=0; b=1; c=0; d=1;
% Dirichlet境界値 b() 調和関数を選ぶとこれが平衡解 (定常解)
function val = dbv(x,y)
val = (x-0.5).*(x-0.5)-(y-0.5).*(y-0.5);
end
% 初期値 (平衡解を境界値が0のもので摂動する)
function val = iv(x,y)
val = dbv(x,y) + sin(pi*x).*sin(2*pi*y);
end
% 厳密解が分かる
function val = exact(x,y,t)
val = dbv(x,y) + exp(- 5 * pi * pi * t) * sin(pi*x).*sin(2*pi*y);
end
% 長方形の下の辺での b()
function val = bbottom(x)
val = dbv(x,c);
end
% 長方形の上の辺での b()
function val = btop(x)
val = dbv(x,d);
end
% 長方形の左の辺での b()
function val = bleft(y)
val = dbv(a, y);
end
% 長方形の右の辺での b()
function val = bright(y)
val = dbv(b, y);
end
% 誤差を測るためのノルム (最大値ノルム)
function val = mynorm(a)
[m,n]=size(a);
val = norm(reshape(a,m*n,1),Inf);
end
% 格子への分割
Nx=100;
Ny=100;
hx=(b-a)/Nx;
hy=(d-c)/Ny;
theta=0.5;
tau=0.5/(1/hx^2+1/hy^2); % 陽解法の場合のギリギリの刻み(陰解法なので大きくてもOK)
lambdax=tau/hx^2;
lambday=tau/hy^2;
disp(sprintf('Nx=%d, Ny=%d, hx=%f, hy=%f, tau=%e, λx=%f, λy=%f', ...
Nx, Ny, hx, hy, tau, lambdax, lambday));
% 差分方程式 A U^{n+1}=B U^n の行列
[A,B]=heat2d_mat(Nx,Ny,lambdax,lambday,theta);
% LU分解
[AL,AU,ap]=lu(A,'vector');
% 格子点の座標ベクトル x=(x_1,x_2,...,x_{Nx+1}), y=(y_1,y_2,...,y_{Ny+1})
X=linspace(a,b,Nx+1)';
Y=linspace(c,d,Ny+1)';
% 格子点のx,y座標の配列 X={X_{ij}}, Y={Y_{ij}}
[x,y]=meshgrid(X,Y);
% 初期値
u= iv(x,y);
% 極限 (境界値 b の式が調和ならば)
ulimit = dbv(x,y);
% 初期値のグラフを描く
disp('初期値のグラフ')
meshc(x,y,u); axis([a b c d -2 2]); drawnow;
U=reshape(u(2:Ny,2:Nx),(Nx-1)*(Ny-1),1);
% 境界値 左の辺、右の辺
blvector=bleft(Y(2:Ny));
brvector=bright(Y(2:Ny));
lindex=1:Ny-1;
rindex=(Nx-2)*(Ny-1)+1:(Nx-1)*(Ny-1);
% 境界値 下の辺、上の辺
bbvector=bbottom(X(2:Nx));
btvector=btop(X(2:Nx));
bindex=1:Ny-1:(Nx-1)*(Ny-1);
tindex=Ny-1:Ny-1:(Nx-1)*(Ny-1);
% どこまで計算するか
Tmax=0.5;
Nmax = ceil(Tmax/tau);
% グラフを表示する時間の間隔
dt=0.005;
skip=dt/tau;
%
disp('ループに入ります。何かキーを押してください。')
pause
t=0;
maxerror=0;
for n=0:Nmax
% 連立1次方程式の右辺の計算
U=B*U;
% 移項
U(lindex)=U(lindex)+lambdax*blvector;
U(rindex)=U(rindex)+lambdax*brvector;
U(bindex)=U(bindex)+lambday*bbvector;
U(tindex)=U(tindex)+lambday*btvector;
% 連立1次方程式を解いて差分解を求める
U=AU\(AL\U(ap,:));
t=(n+1)*tau;
if mod(n,skip)==0
u(2:Ny,2:Nx)=reshape(U,Ny-1,Nx-1);
meshc(x,y,u); axis([a b c d -2 2]); drawnow;
error = mynorm(u-exact(x,y,t));
if error > maxerror maxerror = error; end
disp(sprintf('t=%f, ||error||=%e, ||u||=%e, ||u∞||=%e',...
t, error, mynorm(u), mynorm(ulimit)));
end
end
display(sprintf('||u(・,t)-u(・,∞)||=%e', mynorm(u-ulimit)));
display(sprintf('Nx=%d,Ny=%d,max error=%e', Nx, Ny, maxerror));
end
|
curl -O https://m-katsurada.sakura.ne.jp/program/fdm/heat2d_nh.m cp -p heat2d_nh.m ~/Documents/MATLAB |
>> heat2d_nh >> heat2d_nh(100,100) |