吸収境界条件吸収境界条件
概要
波動の伝播を数値的に計算する方法には有限要素法(FDM)、k-space methodなどがある。これらの方法は波動が無限に伝播することを仮定しているが、実際のシミュレーションでは有限の格子内で波動が伝播する。そのため、境界では波動が反射する問題が生じる。これを解決するためには、実際にシミュレーションしたい領域よりもはるかに広く格子を設定するか、境界条件をうまく設定して波動の反射が起こらないようにする。そのような境界条件を吸収境界条件absorbing boundary condition (ABC)という。
公式
次のような波動方程式を考えよう。
∂t2∂2u(x,t)=c2∂x2∂2u(x,t)for (x,t)∈R×[0,∞)
これを有限な格子空間[0,1]×[0,T]でシミュレーションするとしよう。x軸空間をM等分するとΔx=1/M、時間をN等分するとΔt=T/Nである。波動方程式の解uの離散化をUin=u(i∗Δx,n∗Δt)という。このときi=0,1,⋯,M、n=0,1,⋯,Nである。吸収境界条件は次のようになる。
U0n+1=U1n+(cΔt+Δx)(cΔt−Δx)(U1n+1−U0n)
UMn+1=UM−1n+(cΔt+Δx)(cΔt−Δx)(UM−1n+1−UMn)
説明
1次元と2次元で吸収境界条件を適用した結果は次の通りである。


導出
波動方程式を次のように整理しよう。
⟹⟹∂t2∂2u(x,t)∂t2∂2u(x,t)−c2∂x2∂2u(x,t)(∂t∂−c∂x∂)(∂t∂+c∂x∂)u(x,t)=c2∂x2∂2u(x,t)=0=0
このとき波動方程式の解u(x,t)は次の2つの方程式のいずれかを満たす。
(∂t∂−c∂x∂)u(x,t)=0(1)
(∂t∂+c∂x∂)u(x,t)=0(2)
(1)を満たすu+(x,t)=f(x+ct)は−x方向に移動する波動である。反対に(2)を満たすu−(x,t)=g(x−ct)は+x方向に移動する波動である。境界のx=0の場所ではUは左方向に行く波動だけが存在するはずなので、Uは(1)を満たさなければならない。x=0でのtに対する微分係数を求めると、
∂t∂u∣x=0≈ΔtU0n+1−U0n≈Δt2U0n+1+U1n+1−2U0n+U1n
2つ目の近似式で関数値を隣接する2点の平均としたのは、より良い近似のためである。同様にxに対する微分係数は次のように求めることができる。
c∂x∂u∣x=0≈cΔxU1n−U0n≈cΔx2U1n+1+U1n−2U0n+1+U0n
Uはx=0で(1)を満たさなければならないので、
Δt2U0n+1+U1n+1−2U0n+U1n−cΔx2U1n+1+U1n−2U0n+1+U0n=0
⟹2ΔtU0n+1+U1n+1−U0n−U1n−c2ΔxU1n+1+U1n−U0n+1−U0n=0
⟹U0n+1+U1n+1−U0n−U1n−ΔxcΔt(U1n+1+U1n−U0n+1−U0n)=0
⟹(ΔxcΔt+1)U0n+1=(ΔxcΔt+1)U1n−(U1n+1−U0n)+ΔxcΔt(U1n+1−U0n)
⟹(ΔxcΔt+1)U0n+1=(ΔxcΔt+1)U1n+(ΔxcΔt−1)(U1n+1−U0n)
⟹U0n+1=U1n+(ΔxcΔt+1)(ΔxcΔt−1)(U1n+1−U0n)=U1n+(cΔt+Δx)(cΔt−Δx)(U1n+1−U0n)
同様に、x=1の場合の吸収境界条件は次の通りである。
UMn+1=UM−1n+(cΔt+Δx)(cΔt−Δx)(UM−1n+1−UMn)
■
コード
1次元波動方程式における吸収境界条件をシミュレートしたコードは次の通りである。
using Plots
x = LinRange(-1, 1, 300)
Δx = x[2] - x[1]
t = LinRange(0, 4, 600)
Δt = t[2] - t[1]
μ = Δt / Δx
C = (μ - 1) / (μ + 1)
U = zeros(length(x), length(t))
U[:, 1] = exp.(-30 * x.^2)
U[:, 2] = exp.(-30 * x.^2)
for i ∈ 3:length(t)
U[2:end-1, i] = (μ^2)*U[3:end, i-1] + 2(1-μ^2)*U[2:end-1, i-1] + (μ^2)*U[1:end-2, i-1] - U[2:end-1, i-2]
end
V = zeros(length(x), length(t))
V[:, 1] = exp.(-30 * x.^2)
V[:, 2] = exp.(-30 * x.^2)
for i ∈ 3:length(t)
V[2:end-1, i] = (μ^2)*V[3:end, i-1] + 2(1-μ^2)*V[2:end-1, i-1] + (μ^2)*V[1:end-2, i-1] - V[2:end-1, i-2]
V[1, i] = V[2, i-1] + C*(V[2, i] - V[1, i-1])
V[end, i] = V[end-1, i-1] + C*(V[end-1, i] - V[end, i-1])
end
anim = @animate for i ∈ 1:2:Int(length(t)/3)
p1 = plot(x, U[:, i], xlims=(-1, 1), ylims=(-1,1), legend=false, dpi=300, title = "w/o ABCs")
p2 = plot(x, V[:, i], xlims=(-1, 1), ylims=(-1,1), legend=false, dpi=300, title = "w/ ABCs")
plot(p1, p2, layout=(2,1))
end
gif(anim, fps=40)
2次元での吸収境界条件をシミュレートしたコードは次の通りである。
using Plots
nx, ny = 200, 200
nt = 400
Lx, Ly = 10.0, 10.0
T = 10.0
c = 1.0
dx = Lx / (nx - 1)
dy = Ly / (ny - 1)
dt = T / (nt - 1)
κ = c * dt / dx
u = zeros(Float64, nx, ny, nt)
v = zeros(Float64, nx, ny, nt)
for i ∈ 1:nx, j ∈ 1:ny
if 13^2 < (i-160)^2 + (j-160)^2 < 15^2
u[i, j, 1] = 1
u[i, j, 2] = 1
v[i, j, 1] = 1
v[i, j, 2] = 1
end
if (i-40)^2 + (j-40)^2 < 4^2
u[i, j, 1] = 1
u[i, j, 2] = 1
v[i, j, 1] = 1
v[i, j, 2] = 1
end
end
for n in 2:(nt - 1)
u_xx = (u[3:end, 2:end-1, n] - 2 * u[2:end-1, 2:end-1, n] + u[1:end-2, 2:end-1, n]) / dx^2
u_yy = (u[2:end-1, 3:end, n] - 2 * u[2:end-1, 2:end-1, n] + u[2:end-1, 1:end-2, n]) / dy^2
u[2:end-1, 2:end-1, n + 1] = 2 * u[2:end-1, 2:end-1, n] - u[2:end-1, 2:end-1, n - 1] + c^2 * dt^2 * (u_xx + u_yy)
v_xx = (v[3:end, 2:end-1, n] - 2 * v[2:end-1, 2:end-1, n] + v[1:end-2, 2:end-1, n]) / dx^2
v_yy = (v[2:end-1, 3:end, n] - 2 * v[2:end-1, 2:end-1, n] + v[2:end-1, 1:end-2, n]) / dy^2
v[2:end-1, 2:end-1, n + 1] = 2 * v[2:end-1, 2:end-1, n] - v[2:end-1, 2:end-1, n - 1] + c^2 * dt^2 * (v_xx + v_yy)
u[1, 2:end-1, n+1] = u[2, 2:end-1, n] + ((κ-1)/(κ+1))*(u[2, 2:end-1, n] - u[1, 2:end-1, n-1])
u[end, 2:end-1, n+1] = u[end-1, 2:end-1, n] + ((κ-1)/(κ+1))*(u[end-1, 2:end-1, n] - u[end, 2:end-1, n-1])
u[2:end-1, 1, n+1] = u[2:end-1, 2, n] + ((κ-1)/(κ+1))*(u[2:end-1, 2, n] - u[2:end-1, 1, n-1])
u[2:end-1, end, n+1] = u[2:end-1, end-1, n] + ((κ-1)/(κ+1))*(u[2:end-1, end-1, n] - u[2:end-1, end, n-1])
end
b2r = cgrad([:blue, :white, :red])
anim = @animate for n in 1:nt
p1 = heatmap(u[:, :, n], c=b2r, clim=(-1,1), legend=false, xticks=false, yticks=false, framestyle=:box, ratio=1, dpi=300, title="w/ ABCs")
p2 = heatmap(v[:, :, n], c=b2r, clim=(-1,1), legend=false, xticks=false, yticks=false, framestyle=:box, ratio=1, dpi=300, title="w/o ABCs")
plot(p1, p2, layout=(1,2), size=(728, 728/2))
end
gif(anim, fps=50)