-
-
Notifications
You must be signed in to change notification settings - Fork 243
Description
Building off of the story in #260 , DiffEqOperators getting auto finite difference discretizations likely done this summer. Thus the next step is automating the PDE discretization from a high level description. Since ModelingToolkit.jl is well-developed, it makes sense to utilize that to give the high level description. Thus I put a prototype together:
using ModelingToolkit, DiffEqOperators, DiffEqBase, LinearAlgebra
# Define some variables
@parameters t x
@variables u(..)
@derivatives Dt'~t
@derivatives Dxx''~x
eq = Dt(u(t,x)) ~ Dxx(u(t,x))
bcs = [u(0,x) ~ - x * (x-1) * sin(x),
u(t,0) ~ 0, u(t,1) ~ 0]
abstract type AbstractDomain{T,N} end
struct IntervalDomain{T} <: AbstractDomain{T,1}
lower::T
upper::T
end
struct VarDomainPairing
variables
domain::AbstractDomain
end
Base.:∈(variable::ModelingToolkit.Operation,domain::AbstractDomain) = VarDomainPairing(variable,domain)
Base.:∈(variables::NTuple{N,ModelingToolkit.Operation},domain::AbstractDomain) where N = VarDomainPairing(variables,domain)
domains = [t ∈ IntervalDomain(0.0,1.0),
x ∈ IntervalDomain(0.0,1.0)]
#########################################
# Note: Variables can be multidimensional
struct ProductDomain{D,T,N} <: AbstractDomain{T,N}
domains::D
end
⊗(args::AbstractDomain{T}...) where T = ProductDomain{typeof(args),T,length(args)}(args)
@parameters z
z ∈ (IntervalDomain(0.0,1.0) ⊗ IntervalDomain(0.0,1.0))
@register Base.getindex(x,i)
Base.getindex(x::Operation,i::Int64) = Operation(getindex,[x,i])
z[1]^2 + z[2]^2 ~ 1
z[1]^2 + z[2]^2 < 1
struct CircleDomain <: AbstractDomain{Float64,2}
polar::Bool
CircleDomain(polar=false) = new(polar)
end
z ∈ CircleDomain()
@parameters y
(x,y) ∈ CircleDomain()
@parameters r θ
(r,θ) ∈ CircleDomain(true)
# Use constrained equations for more detailed BCs
struct ConstrainedEquation
constraints
eq
end
ConstrainedEquation([x ~ 0,y < 1/2], u(t,x,y) ~ x + y^2)
#########################################
struct PDESystem <: ModelingToolkit.AbstractSystem
eq
bcs
domain
indvars
depvars
end
pdesys = PDESystem(eq,bcs,domains,[t,x],[u])
abstract type AbstractDiscretization end
struct MOLFiniteDifference{T} <: AbstractDiscretization
dxs::T
order::Int
end
MOLFiniteDifference(args...;order=2) = MOLFiniteDifference(args,order)
discretization = MOLFiniteDifference(0.1)
struct PDEProblem{P,E,S} <: DiffEqBase.DEProblem
prob::P
extrapolation::E
space::S
end
function Base.show(io::IO, A::PDEProblem)
println(io,summary(A.prob))
println(io)
end
Base.summary(prob::PDEProblem) = string(DiffEqBase.TYPE_COLOR,
nameof(typeof(prob)),
DiffEqBase.NO_COLOR)
function discretize(pdesys,
discretization::MOLFiniteDifference)
tdomain = pdesys.domain[1].domain
domain = pdesys.domain[2].domain
@assert domain isa IntervalDomain
len = domain.upper - domain.lower
dx = discretization.dxs[1]
interior = domain.lower+dx:dx:domain.upper-dx
X = domain.lower:dx:domain.upper
L = CenteredDifference(2,2,dx,Int(len/dx)-2)
Q = DiffEqOperators.DirichletBC([0.0,0.0],[1.0,1.0])
function f(du,u,p,t)
mul!(du,L,Array(Q*u))
end
u0 = @. - interior * (interior - 1) * sin(interior)
PDEProblem(ODEProblem(f,u0,(tdomain.lower,tdomain.upper),nothing),Q,X)
end
prob = discretize(pdesys,discretization)
function DiffEqBase.solve(prob::PDEProblem,alg::DiffEqBase.DEAlgorithm,args...;
kwargs...)
solve(prob.prob,alg,args...;kwargs...)
end
using OrdinaryDiffEq
sol = solve(prob,Tsit5(),saveat=0.1)
using Plots
plot(prob.space,Array(prob.extrapolation*sol[1]))
plot!(prob.space,Array(prob.extrapolation*sol[2]))
plot!(prob.space,Array(prob.extrapolation*sol[3]))
plot!(prob.space,Array(prob.extrapolation*sol[4]))Description of the code
The equation and boundary conditions are just given by ModelingToolkit.jl operations. The ConstrainedEquation allows for specifying equations only on specific parts. Then each independent variable needs a domain. Domains can be multi-dimensional, and variables can be multi-dimensional, so this lets us smartly define variables in domains (and in manifolds!) and equations using them.
The totality of the PDE is in the PDESystem, just a ModelingToolkit.AbstractSystem object. The workflow for solving a PDE is thus:
- Define a PDESystem.
- Discretize to a PDEProblem with an AbstractDiscretization. This step will kick out things like a PDEProblem that wraps an ODEProblem, or SDEProblem, or even LinearProblem and NonlinearProblem.
- Solve the PDEProblem.
So (1) is all ModelingToolkit.jl. (2) is just a discretize function and some problem types. We will need to add LinearProblem and NonlinearProblem with some solve dispatches for this. After (3), we will want to wrap the solution so it can get a nicer plot recipe.
Thus the steps are:
- Add domains and VarDomainPairing to ModelingToolkit
- Add ConstrainedEquation to ModelingToolkit
- Implement PDESystem in ModelingToolkit
- Implement LinearProblem and NonlinearProblem in DiffEqBase
- Implement some basic DEAlgorithms for it. Solve dispatches on DEAlgorithms that just do
\, GMRES (LinSolveGMRES()),nlsolve - Implement PDEProblem, PDETimeseriesSolution, PDENoTimeseriesSolution
- Create
discretizestub function in DiffEqBase - Implement a smart
discretizefunction in DiffEqOperators.jl for automatic finite difference discretizations. This is where a ton of work will have to go, since it will need to parse the equation and find out whatCenteredDifferenceandUpwindDifferenceoperators to create. This work will continue after this closes.