Skip to content
This repository was archived by the owner on Jul 19, 2023. It is now read-only.

Commit 343cf32

Browse files
Merge pull request #400 from akashkgarg/issue-282-nonlinearproblem
creating nonlinear problem with ImplicitFiniteDifference
2 parents a730c15 + 05d0e95 commit 343cf32

5 files changed

Lines changed: 230 additions & 17 deletions

File tree

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ LazyBandedMatrices = "d7e5e226-e90b-4449-9968-0f923699bf6f"
1414
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1515
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
1616
NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd"
17+
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
1718
RuntimeGeneratedFunctions = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47"
1819
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1920
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
@@ -36,6 +37,7 @@ SciMLBase = "1.11"
3637
StaticArrays = "0.10, 0.11, 0.12, 1.0"
3738
SymbolicUtils = "0.11, 0.12"
3839
julia = "1"
40+
NonlinearSolve = "0.3.7"
3941

4042
[extras]
4143
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"

docs/src/symbolic_tutorials/mol_heat.md

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ In this tutorial we will use the symbolic interface to solve the heat equation.
77
### Dirichlet boundary conditions
88

99
```julia
10-
using OrdinaryDiffEq, ModelingToolkit, DiffEqOperators
10+
using OrdinaryDiffEq, ModelingToolkit, DiffEqOperators, DomainSets
1111
# Method of Manufactured Solutions: exact solution
1212
u_exact = (x,t) -> exp.(-t) * cos.(x)
1313

@@ -59,7 +59,7 @@ savefig("plot.png")
5959
### Neumann boundary conditions
6060

6161
```julia
62-
using OrdinaryDiffEq, ModelingToolkit, DiffEqOperators
62+
using OrdinaryDiffEq, ModelingToolkit, DiffEqOperators, DomainSets
6363
# Method of Manufactured Solutions: exact solution
6464
u_exact = (x,t) -> exp.(-t) * cos.(x)
6565

@@ -114,7 +114,7 @@ savefig("plot.png")
114114
### Robin boundary conditions
115115

116116
```julia
117-
using OrdinaryDiffEq, ModelingToolkit, DiffEqOperators
117+
using OrdinaryDiffEq, ModelingToolkit, DiffEqOperators, DomainSets
118118
# Method of Manufactured Solutions
119119
u_exact = (x,t) -> exp.(-t) * sin.(x)
120120

@@ -204,3 +204,77 @@ anim = @animate for i in 1:length(t)
204204
end
205205
gif(anim, "plot.gif",fps=30)
206206
```
207+
208+
### Stationary Problems
209+
210+
```julia
211+
using ModelingToolkit,DiffEqOperators,LinearAlgebra,DomainSets
212+
using ModelingToolkit: Differential
213+
using DifferentialEquations
214+
215+
@parameters x
216+
@variables u(..)
217+
Dxx = Differential(x)^2
218+
219+
eq = Dxx(u(x)) ~ 0
220+
dx = 0.1
221+
222+
bcs = [u(0) ~ 1,
223+
u(1) ~ 2]
224+
225+
# Space and time domains
226+
domains = [x Interval(0.0,1.0)]
227+
228+
pdesys = PDESystem([eq],bcs,domains,[x],[u(x)])
229+
230+
# Note that we pass in `nothing` for the time variable `t` here since we
231+
# are creating a stationary problem without a dependence on time, only space.
232+
discretization = MOLFiniteDifference([x=>dx], nothing, centered_order=2)
233+
prob = discretize(pdesys,discretization)
234+
sol = solve(prob)
235+
236+
using Plots
237+
xs = domains[1].domain.lower:dx:domains[1].domain.upper
238+
plot(xs, sol.u)
239+
240+
```
241+
242+
```julia
243+
using ModelingToolkit,DiffEqOperators,LinearAlgebra,DomainSets
244+
using ModelingToolkit: Differential
245+
using DifferentialEquations
246+
247+
@parameters x y
248+
@variables u(..)
249+
Dxx = Differential(x)^2
250+
Dyy = Differential(y)^2
251+
252+
eq = Dxx(u(x, y)) + Dyy(u(x, y))~ 0
253+
dx = 0.1
254+
dy = 0.1
255+
256+
bcs = [u(0,y) ~ 0.0,
257+
u(1,y) ~ y,
258+
u(x,0) ~ 0.0,
259+
u(x,1) ~ x]
260+
261+
# Space and time domains
262+
domains = [x Interval(0.0,1.0),
263+
y Interval(0.0,1.0)]
264+
265+
pdesys = PDESystem([eq],bcs,domains,[x,y],[u(x,y)])
266+
267+
# Note that we pass in `nothing` for the time variable `t` here since we
268+
# are creating a stationary problem without a dependence on time, only space.
269+
discretization = MOLFiniteDifference([x=>dx,y=>dy], nothing, centered_order=2)
270+
271+
prob = discretize(pdesys,discretization)
272+
sol = solve(prob)
273+
274+
using Plots
275+
xs,ys = [infimum(d.domain):dx:supremum(d.domain) for d in domains]
276+
u_sol = reshape(sol.u, (length(xs),length(ys)))
277+
plot(xs, ys, u_sol, linetype=:contourf,title = "solution")
278+
```
279+
![solution](https://user-images.githubusercontent.com/620651/121951649-0b701e00-cd10-11eb-8af8-b203f7f13abc.png)
280+

src/MOLFiniteDifference/MOL_discretization.jl

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import DomainSets
44
# Method of lines discretization scheme
55

66
@enum GridAlign center_align edge_align
7+
78
struct MOLFiniteDifference{T,T2} <: DiffEqBase.AbstractDiscretization
89
dxs::T
910
time::T2
@@ -45,10 +46,13 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
4546
pdeeqs = pdesys.eqs isa Vector ? pdesys.eqs : [pdesys.eqs]
4647
t = discretization.time
4748
# Get tspan
48-
tdomain = pdesys.domain[findfirst(d->isequal(t.val, d.variables),pdesys.domain)]
49-
@assert tdomain.domain isa DomainSets.Interval
50-
tspan = (tdomain.domain.lower,tdomain.domain.upper)
51-
49+
tspan = nothing
50+
if t != nothing
51+
tdomain = pdesys.domain[findfirst(d->isequal(t.val, d.variables),pdesys.domain)]
52+
@assert tdomain.domain isa DomainSets.Interval
53+
tspan = (DomainSets.infimum(tdomain.domain), DomainSets.supremum(tdomain.domain))
54+
end
55+
5256
depvar_ops = map(x->operation(x.val),pdesys.depvars)
5357

5458
u0 = []
@@ -66,7 +70,7 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
6670
# Read the independent variables,
6771
# ignore if the only argument is [t]
6872
allindvars = Set(filter(xs->!isequal(xs,[t]),map(arguments,depvars)))
69-
allnottime = Set(filter(!isempty,map(u->filter(x->!isequal(x,t.val),arguments(u)),depvars)))
73+
allnottime = Set(filter(!isempty,map(u->filter(x-> t == nothing || !isequal(x,t.val),arguments(u)),depvars)))
7074
if isempty(allnottime)
7175
push!(alleqs,eq)
7276
push!(alldepvarsdisc,depvars)
@@ -87,7 +91,7 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
8791
space = map(nottime) do x
8892
xdomain = pdesys.domain[findfirst(d->isequal(x, d.variables),pdesys.domain)]
8993
dx = discretization.dxs[findfirst(dxs->isequal(x, dxs[1].val),discretization.dxs)][2]
90-
dx isa Number ? (xdomain.domain.lower:dx:xdomain.domain.upper) : dx
94+
dx isa Number ? (DomainSets.infimum(xdomain.domain):dx:DomainSets.supremum(xdomain.domain)) : dx
9195
end
9296
dxs = map(nottime) do x
9397
dx = discretization.dxs[findfirst(dxs->isequal(x, dxs[1].val),discretization.dxs)][2]
@@ -112,7 +116,9 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
112116
space_indices = CartesianIndices(((axes(s)[1] for s in space)...,))
113117
grid_indices = CartesianIndices(((axes(g)[1] for g in grid)...,))
114118
depvarsdisc = map(depvars) do u
115-
if isequal(arguments(u),[t])
119+
if t == nothing
120+
[Num(Variable{Real}(Base.nameof(operation(u)),II.I...)) for II in grid_indices]
121+
elseif isequal(arguments(u),[t])
116122
[u for II in grid_indices]
117123
else
118124
[Num(Variable{Symbolics.FnType{Tuple{Any}, Real}}(Base.nameof(operation(u)),II.I...))(t) for II in grid_indices]
@@ -134,7 +140,10 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
134140

135141
bclocs = map(e->substitute.(indvars,e),edgevals) # location of the boundary conditions e.g. (t,0.0,y)
136142
edgemaps = Dict(bclocs .=> [spacevals[e...] for e in edges])
137-
initmaps = substitute.(depvars,[t=>tspan[1]])
143+
initmaps = depvars
144+
if t != nothing
145+
initmaps = substitute.(depvars,[t=>tspan[1]])
146+
end
138147

139148
# Generate map from variable (e.g. u(t,0)) to discretized variable (e.g. u₁(t))
140149
subvar(depvar) = substitute.((depvar,),edgevals)
@@ -179,7 +188,7 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
179188
for bc in pdesys.bcs
180189
bcdepvar = first(get_depvars(bc.lhs, depvar_ops))
181190
if any(u->isequal(operation(u),operation(bcdepvar)),depvars)
182-
if operation(bc.lhs) isa Sym && !any(x -> isequal(x, t.val), arguments(bc.lhs))
191+
if t != nothing && operation(bc.lhs) isa Sym && !any(x -> isequal(x, t.val), arguments(bc.lhs))
183192
# initial condition
184193
# Assume in the form `u(...) ~ ...` for now
185194
i = findfirst(isequal(bc.lhs),initmaps)
@@ -334,7 +343,7 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
334343
push!(alldepvarsdisc,reduce(vcat,depvarsdisc))
335344
end
336345
end
337-
u0 = reduce(vcat,u0)
346+
u0 = !isempty(u0) ? reduce(vcat,u0) : u0
338347
bceqs = reduce(vcat,bceqs)
339348
alleqs = reduce(vcat,alleqs)
340349
alldepvarsdisc = unique(reduce(vcat,alldepvarsdisc))
@@ -343,12 +352,25 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
343352
defaults = pdesys.ps === nothing || pdesys.ps === SciMLBase.NullParameters() ? u0 : vcat(u0,pdesys.ps)
344353
ps = pdesys.ps === nothing || pdesys.ps === SciMLBase.NullParameters() ? Num[] : first.(pdesys.ps)
345354
# Combine PDE equations and BC equations
346-
sys = ODESystem(vcat(alleqs,unique(bceqs)),t,vec(reduce(vcat,vec(alldepvarsdisc))),ps,defaults=Dict(defaults))
347-
sys, tspan
355+
if t == nothing
356+
# At the time of writing, NonlinearProblems require that the system of equations be in this form:
357+
# 0 ~ ...
358+
# Thus, before creating a NonlinearSystem we normalize the equations s.t. the lhs is zero.
359+
eqs = map(eq -> 0 ~ eq.rhs - eq.lhs, vcat(alleqs,unique(bceqs)))
360+
sys = NonlinearSystem(eqs,vec(reduce(vcat,vec(alldepvarsdisc))),ps,defaults=Dict(defaults))
361+
return sys, nothing
362+
else
363+
sys = ODESystem(vcat(alleqs,unique(bceqs)),t,vec(reduce(vcat,vec(alldepvarsdisc))),ps,defaults=Dict(defaults))
364+
return sys, tspan
365+
end
348366
end
349367

350368
function SciMLBase.discretize(pdesys::ModelingToolkit.PDESystem,discretization::DiffEqOperators.MOLFiniteDifference)
351369
sys, tspan = SciMLBase.symbolic_discretize(pdesys,discretization)
352-
simpsys = structural_simplify(sys)
353-
prob = ODEProblem(simpsys,Pair[],tspan)
370+
if tspan == nothing
371+
return prob = NonlinearProblem(sys, ones(length(sys.states)))
372+
else
373+
simpsys = structural_simplify(sys)
374+
return prob = ODEProblem(simpsys,Pair[],tspan)
375+
end
354376
end

test/MOL/MOL_NonlinearProblem.jl

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# 1D diffusion problem
2+
3+
# Packages and inclusions
4+
using ModelingToolkit,DiffEqOperators,LinearAlgebra,Test
5+
using ModelingToolkit: Differential
6+
using NonlinearSolve
7+
using DomainSets
8+
9+
10+
# Handrolled discrete laplace problem
11+
@testset "Hand rolled 1D Laplace" begin
12+
@variables a b c d
13+
eqs = [0 ~ a - 1,
14+
0 ~ a + c - 2*b,
15+
0 ~ b + d - 2*c,
16+
0 ~ d - 1]
17+
ns = NonlinearSystem(eqs, [a, b, c, d], [])
18+
f = eval(generate_function(ns, [a, b, c, d])[2])
19+
prob = NonlinearProblem(ns, zeros(4), [])
20+
sol = NonlinearSolve.solve(prob, NewtonRaphson())
21+
@test sol.u ones(4)
22+
end
23+
24+
# Laplace's Equation, same as above but with MOL discretization
25+
@testset "1D Laplace - constant solution" begin
26+
@parameters x
27+
@variables u(..)
28+
Dxx = Differential(x)^2
29+
30+
eq = Dxx(u(x)) ~ 0
31+
dx = 1/3
32+
33+
bcs = [u(0) ~ 1,
34+
u(1) ~ 1]
35+
36+
# Space and time domains
37+
domains = [x Interval(0.0,1.0)]
38+
39+
pdesys = PDESystem([eq],bcs,domains,[x],[u(x)])
40+
discretization = MOLFiniteDifference([x=>dx], nothing, centered_order=2)
41+
prob = discretize(pdesys,discretization)
42+
sol = NonlinearSolve.solve(prob, NewtonRaphson())
43+
44+
@test sol.u ones(4)
45+
end
46+
47+
# Laplace's Equation, linear solution
48+
@testset "1D Laplace - linear solution" begin
49+
@parameters x
50+
@variables u(..)
51+
Dxx = Differential(x)^2
52+
53+
eq = Dxx(u(x)) ~ 0
54+
dx = 0.1
55+
56+
bcs = [u(0) ~ 1,
57+
u(1) ~ 2]
58+
59+
# Space and time domains
60+
domains = [x Interval(0.0,1.0)]
61+
62+
pdesys = PDESystem([eq],bcs,domains,[x],[u(x)])
63+
discretization = MOLFiniteDifference([x=>dx], nothing, centered_order=2)
64+
prob = discretize(pdesys,discretization)
65+
sol = NonlinearSolve.solve(prob, NewtonRaphson())
66+
67+
@test sol.u 1.0:0.1:2.0
68+
end
69+
70+
# 2D heat
71+
@testset "2D heat equation" begin
72+
@parameters x y
73+
@variables u(..)
74+
Dxx = Differential(x)^2
75+
Dyy = Differential(y)^2
76+
77+
eq = Dxx(u(x, y)) + Dyy(u(x, y))~ 0
78+
dx = 0.1
79+
dy = 0.1
80+
81+
bcs = [u(0,y) ~ x*y,
82+
u(1,y) ~ x*y,
83+
u(x,0) ~ x*y,
84+
u(x,1) ~ x*y]
85+
86+
87+
# Space and time domains
88+
domains = [x Interval(0.0,1.0),
89+
y Interval(0.0,1.0)]
90+
91+
pdesys = PDESystem([eq],bcs,domains,[x,y],[u(x,y)])
92+
93+
# Note that we pass in `nothing` for the time variable `t` here since we
94+
# are creating a stationary problem without a dependence on time, only space.
95+
discretization = MOLFiniteDifference([x=>dx,y=>dy], nothing, centered_order=2)
96+
97+
prob = discretize(pdesys,discretization)
98+
sol = NonlinearSolve.solve(prob, NewtonRaphson())
99+
xs,ys = [infimum(d.domain):dx:supremum(d.domain) for d in domains]
100+
u_sol = reshape(sol.u, (length(xs),length(ys)))
101+
102+
# test boundary
103+
@test all(abs.(u_sol[:,1]) .< eps(Float32))
104+
@test all(abs.(u_sol[1,:]) .< eps(Float32))
105+
@test u_sol[:,end] 0:dy:1.0
106+
@test u_sol[end,:] 0:dx:1.0
107+
108+
# test interior with finite differences
109+
interior = CartesianIndices((axes(xs)[1], axes(ys)[1]))[2:end-1,2:end-1]
110+
fd = map(interior) do I
111+
abs(u_sol[(I - CartesianIndex(1, 0))] + u_sol[(I + CartesianIndex(1,0))] - 2*u_sol[I]) < eps(Float32)
112+
end
113+
@test all(fd)
114+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ if GROUP == "All" || GROUP == "MOLFiniteDifference"
4343
@time @safetestset "MOLFiniteDifference Interface: 2D Diffusion" begin include("MOL/MOL_2D_Diffusion.jl") end
4444
@time @safetestset "MOLFiniteDifference Interface: 1D HigherOrder" begin include("MOL/MOL_1D_HigherOrder.jl") end
4545
@time @safetestset "MOLFiniteDifference Interface: 1D Partial DAE" begin include("MOL/MOL_1D_PDAE.jl") end
46+
@time @safetestset "MOLFiniteDifference Interface: Stationary Nonlinear Problems" begin include("MOL/MOL_NonlinearProblem.jl") end
4647
end
4748

4849
if GROUP == "All" || GROUP == "Misc"

0 commit comments

Comments
 (0)