用隐式欧拉和共轭梯度线性求解器求解带非零Dirichlet BC的热方程?

时间:2019-02-06 02:13:09

标签: julia differential-equations differentialequations.jl

许多用户问过如何使用非零Dirichlet BC和内部线性求解器的共轭梯度来求解热方程u_t = u_xx。在转向更困难的抛物线形PDE之前,这是一个常见的简化PDE问题。在DifferentialEquations.jl中如何完成?

1 个答案:

答案 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])

enter image description here

现在,我们将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])

enter image description here

现在让我们换掉线性求解器。 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方法。