许多用户询问如何使用非零 Dirichlet BC 和内部线性求解器的共轭梯度求解热方程 u_t = u_xx。在转向更困难的抛物线 PDE 版本之前,这是一个常见的简化 PDE 问题。这是如何在DifferentialEquations.jl 中完成的?
问问题
340 次
1 回答
7
让我们逐步解决这个问题。首先,让我们用 Dirichlet BC 构建离散热方程的线性算子。可以在此 Wiki 页面上找到关于离散化的讨论,该页面表明中心差分方法给出了二阶导数的二阶离散化(u[i-1] - 2u[i] + u[i+1])/dx^2
。这与乘以 的三对角矩阵相同[1 -2 1]*(1/dx^2)
,所以让我们从构建这个矩阵开始:
using LinearAlgebra, OrdinaryDiffEq
x = collect(-π : 2π/511 : π)
## Dirichlet 0 BCs
u0 = @. -(x).^2 + π^2
n = length(x)
A = 1/(2π/511)^2 * Tridiagonal(ones(n-1),-2ones(n),ones(n-1))
请注意,我们已经隐式简化了结尾,因为(u[0] - 2u[1] + u[2])/dx^2 = (- 2u[1] + u[2])/dx^2
当左 BC 为零时,该术语从 matmul 中删除。然后我们使用导数的这种离散化来求解热方程:
function f(du,u,A,t)
mul!(du,A,u)
end
prob = ODEProblem(f,u0,(0.0,10.0),A)
sol = solve(prob,ImplicitEuler())
using Plots
plot(sol[1])
plot!(sol[end])
现在我们使 BC 不为零。请注意,我们只需要添加回u[0]/dx^2
我们之前丢弃的,所以我们有:
## Dirichlet non-zero BCs
## Note that the operator is no longer linear
## To handle affine BCs, we add the dropped term
u0 = @. (x - 0.5).^2 + 1/12
n = length(x)
A = 1/(2π/511)^2 * Tridiagonal(ones(n-1),-2ones(n),ones(n-1))
function f(du,u,A,t)
mul!(du,A,u)
# Now do the affine part of the BCs
du[1] += 1/(2π/511)^2 * u0[1]
du[end] += 1/(2π/511)^2 * u0[end]
end
prob = ODEProblem(f,u0,(0.0,10.0),A)
sol = solve(prob,ImplicitEuler())
plot(sol[1])
plot!(sol[end])
现在让我们换掉线性求解器。文档建议您应该LinSolveCG
在此处使用,如下所示:
sol = solve(prob,ImplicitEuler(linsolve=LinSolveCG()))
这有一些优点,因为它具有有助于调节的规范处理。但是,文档还指出您可以构建自己的线性求解器例程。这是通过提供一个Val{:init}
返回用作线性求解器的类型的调度来完成的,所以我们这样做:
## Create a linear solver for CG
using IterativeSolvers
function linsolve!(::Type{Val{:init}},f,u0;kwargs...)
function _linsolve!(x,A,b,update_matrix=false;kwargs...)
cg!(x,A,b)
end
end
sol = solve(prob,ImplicitEuler(linsolve=linsolve!))
plot(sol[1])
plot!(sol[end])
我们得到了用于线性求解器的具有 Krylov 方法(共轭梯度)的非零 Dirichlet 热方程,使其成为 Newton-Krylov 方法。
于 2019-02-06T02:13:09.157 回答