许多用户问过如何使用非零Dirichlet BC和内部线性求解器的共轭梯度来求解热方程u_t = u_xx。在转向更困难的抛物线形PDE之前,这是一个常见的简化PDE问题。在DifferentialEquations.jl中如何完成?
答案 0 :(得分:3)
让我们逐步解决此问题。首先,让我们为Dirichlet BC建立离散热方程的线性算子。对离散化can be found on this Wiki page的讨论表明,中心差分方法通过(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])
现在让我们换掉线性求解器。 The documentation指出,您只给出了一个Val{:init}
分派,该分派返回了用作线性求解器的类型,所以我们这样做:
## Create a linear solver for GMRES
using IterativeSolvers
function linsolve!(::Type{Val{:init}},f,u0)
function _linsolve!(x,A,b,update_matrix=false)
cg!(x,A,b)
end
end
sol = solve(prob,ImplicitEuler(linsolve=linsolve!))
plot(sol[1])
plot!(sol[end])
对于线性求解器,我们有一个非零Dirichlet热方程,带有Krylov方法(共轭梯度),使其成为Newton-Krylov方法。