差分法の部分を陰解法 (いわゆる
法) に置き換えたプログラム
heat1d_i.jl を紹介する
(可視化のやり方については heat1d_e.jl と同じである)。
この項も、アルゴリズムの詳細については、 「発展系の数値解析」 などを参照すること。
2つのプログラムを示す。まずは LU 分解を自前の関数で実現したもの。
# heat1d_i.jl --- 1次元熱伝導方程式の初期値境界値問題を差分法(陰解法)で解く
#= Julia v1.3 で動作確認 (2019/12/29)
curl -O -O http://nalab.mind.meiji.ac.jp/~mk/misc/20191229/heat1d_i_v2.jl
using Printf
using Plots
gr()
include("heat1d_i.jl")
heat1d_i(0.5, 0.01, 100, 0.5)
=#
function u0(x)
if x < 0.5
x
else
1.0-x
end
end
# 三重対角行列のLU分解
function trilu1(al, ad, au)
n=size(ad)[1]
for i=1:n-1
al[i+1] /= ad[i]
ad[i+1] -= au[i] * al[i+1]
end
end
# LU分解済みの三重対角行列を元に三項方程式を解く
function trisol1(al,ad,au,b)
n=size(ad)[1]
for i=1:n-1
b[i+1] -= b[i] * al[i+1]
end
b[n] /= ad[n]
for i=n-1:-1:1
b[i] = (b[i] - au[i] * b[i + 1]) / ad[i]
end
end
function heat1d_i(tMax=0.5, dt=0.01, N=100, lambda=0.5)
# tMax=0.5; dt=0.01; N=100; lambda=0.5
h=1.0/N
tau=lambda*h*h
nMax = Int(ceil(tMax / tau))
nskip = max(Int(round(dt/tau)),1)
theta=0.5
# 格子点
x=range(0.0,1.0,length=N+1)
# 初期値
u=u0.(x)
tstr=@sprintf("1D heat equation u_t=u_{xx}, t=%.3f",0.0)
p=plot(x,u,title=tstr,xaxis=("x"),yaxis=("u",(-0.02,0.5)),legend=false)
display(p)
sleep(1)
#
println("N=$N, tMax=$tMax, h=$h, tau=$tau, lambda=$lambda, nMax=$nMax")
# 連立1次方程式の係数行列
ad = (1.0+2.0*theta*lambda)*ones(N-1)
al = - theta*lambda*ones(N-1)
au = - theta*lambda*ones(N-1)
trilu1(al,ad,au)
# 連立1次方程式の右辺の準備
c3=1.0-2.0*(1.0-theta)*lambda
c4=(1.0-theta)*lambda
# ベクトルの用意 (不要かも)
# b=zeros(N-1)
newu=similar(u)
for n=1:nMax
t=n*tau
b=c3*u[2:end-1]+c4*(u[1:end-2]+u[3:end])
trisol1(al,ad,au,b)
u[2:end-1]=b
u[1] = u[end] = 0.0 # 同次Dirichlet境界条件
# println(u)
if (n % nskip == 0 || n == nMax)
tstr=@sprintf("1D heat equation u_t=u_{xx}, t=%.3f",t)
p=plot(x,u,title=tstr,xaxis=("x"),yaxis=("u",(-0.02,0.5)),legend=false)
display(p)
sleep(0.01)
end
end
end
実は、heat1d_i.jl では、 最初 al = au = -theta*lambda*ones(N-1) としていて、 うまく動かず、原因に気づくまで苦労した。 al と au は最初は同じ値を持つが、 LU分解した後は値が変わるはずで、 al を au の参照にしてはいけない。上のプログラムのように、 個別に -theta*lambda*ones(N-1) を代入するとか、 au = copy(al) と明示的にコピーするとか、すべきである。
書き始めてしばらくは、 三重対角行列のLU分解をする関数の使い方が探せなかったので、 heat1d_i.jl では、 自前で用意した trulu1(), trusol1() で、 その処理をしている (これらはC言語で書いたコードを移植したものである)。 その後、LU 分解を標準ライブラリィに含まれる LinearAlgebra の中の Tridiagonal() を発見して作ったのが、 次の heat11d_i_v2.jl である。
# heat1d_i_v2.jl --- 1次元熱伝導方程式の初期値境界値問題を差分法(陰解法)で解く
#= Julia v1.3 で動作確認 (2020/1/2)
curl -O http://nalab.mind.meiji.ac.jp/~mk/misc/20191229/heat1d_i_v2.jl
using Printf
using Plots
gr()
using LinearAlgebra
include("heat1d_i_v2.jl")
heat1d_i(0.5, 0.01, 100, 0.5)
=#
function u0(x)
if x < 0.5
x
else
1.0-x
end
end
function heat1d_i_v2(tMax=0.5, dt=0.01, N=100, lambda=0.5)
# tMax=0.5; dt=0.01; N=100; lambda=0.5
h=1.0/N
tau=lambda*h*h
nMax = Int(ceil(tMax / tau))
nskip = max(Int(round(dt/tau)),1)
theta=0.5
# 格子点
x=range(0.0,1.0,length=N+1)
# 初期値
u=u0.(x)
tstr=@sprintf("1D heat equation u_t=u_{xx}, t=%.3f",0.0)
p=plot(x,u,title=tstr,xaxis=("x"),yaxis=("u",(-0.02,0.5)),legend=false)
display(p)
sleep(1)
#
println("N=$N, tMax=$tMax, h=$h, tau=$tau, lambda=$lambda, nMax=$nMax")
# 連立1次方程式の係数行列
ad = (1.0+2.0*theta*lambda)*ones(N-1)
al = - theta*lambda*ones(N-2)
au = - theta*lambda*ones(N-2)
a = Tridiagonal(al,ad,au)
L,U=lu(a)
# 連立1次方程式の右辺の準備
c3=1.0-2.0*(1.0-theta)*lambda
c4=(1.0-theta)*lambda
# ベクトルの用意 (不要かも)
#b=zeros(N-1)
for n=1:nMax
t=n*tau
b=c3*u[2:end-1]+c4*(u[1:end-2]+u[3:end])
u[2:end-1]=U\(L\b)
u[1] = u[end] = 0.0 # 同次Dirichlet境界条件
# println(u)
if (n % nskip == 0 || n == nMax)
tstr=@sprintf("1D heat equation u_t=u_{xx}, t=%.3f",t)
p=plot(x,u,title=tstr,xaxis=("x"),yaxis=("u",(-0.02,0.5)),legend=false)
display(p)
sleep(0.01)
end
end
end
| ターミナルで |
$ curl -O http://nalab.mind.meiji.ac.jp/~mk/misc/20191229/heat1d_i_v2.jl
$ julia
julia> using Printf
julia> using Plots
julia> gr()
julia> using LinearAlgebra
julia> include("heat1d_i_v2.jl")
julia> heat1d_i_v2(0.5, 0.01, 100, 0.5)
|