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

Commit ebd7d9a

Browse files
#351 fix Neumann (and hence Robin) BCs
1 parent 31e316e commit ebd7d9a

2 files changed

Lines changed: 37 additions & 166 deletions

File tree

src/MOLFiniteDifference/MOL_discretization.jl

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
1717
nottime = filter(x->x.val != t.val,pdesys.indvars)
1818

1919
# Discretize space
20-
2120
space = map(nottime) do x
2221
xdomain = pdesys.domain[findfirst(d->x.val == d.variables,pdesys.domain)]
2322
@assert xdomain.domain isa IntervalDomain
2423
dx = discretization.dxs[findfirst(dxs->x.val == dxs[1].val,discretization.dxs)][2]
2524
dx isa Number ? (xdomain.domain.lower:dx:xdomain.domain.upper) : dx
2625
end
26+
# Get tspan
2727
tdomain = pdesys.domain[findfirst(d->t.val == d.variables,pdesys.domain)]
2828
@assert tdomain.domain isa IntervalDomain
2929
tspan = (tdomain.domain.lower,tdomain.domain.upper)
@@ -35,7 +35,9 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
3535
end
3636
spacevals = map(y->[Pair(nottime[i],space[i][y.I[i]]) for i in 1:length(nottime)],indices)
3737

38-
# Build symbolic maps
38+
39+
### INITIAL AND BOUNDARY CONDITIONS ###
40+
# Build symbolic maps for boundaries
3941
edges = reduce(vcat,[[vcat([Colon() for j in 1:i-1],1,[Colon() for j in i+1:length(nottime)]),
4042
vcat([Colon() for j in 1:i-1],size(depvars[1],i),[Colon() for j in i+1:length(nottime)])] for i in 1:length(nottime)])
4143

@@ -45,19 +47,29 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
4547
edgemaps = [spacevals[e...] for e in edges]
4648
initmaps = substitute.(pdesys.depvars,[t=>tspan[1]])
4749

48-
depvarmaps = reduce(vcat,[substitute.((pdesys.depvars[i],),edgevals) .=> edgevars[i] for i in 1:length(pdesys.depvars)])
50+
# Generate map from variable (e.g. u(t,0)) to discretized variable (e.g. u₁(t))
51+
subvar(depvar) = substitute.((depvar,),edgevals)
52+
depvarmaps = reduce(vcat,[subvar(depvar) .=> edgevars[i] for (i, depvar) in enumerate(pdesys.depvars)])
53+
# depvarderivmaps will dictate what to replace the Differential terms with
4954
if length(nottime) == 1
50-
left_weights(j) = DiffEqOperators.calculate_weights(discretization.upwind_order, 0.0, [space[j][1],space[j][2]])
51-
right_weights(j) = DiffEqOperators.calculate_weights(discretization.upwind_order, 0.0, [space[j][end-1],space[j][end]])
55+
# 1D system
56+
left_weights(j) = DiffEqOperators.calculate_weights(discretization.upwind_order, 0.0, space[j][1:2])
57+
right_weights(j) = DiffEqOperators.calculate_weights(discretization.upwind_order, 0.0, space[j][end-1:end])
5258
central_neighbor_idxs(i,j) = [i+CartesianIndex([ifelse(l==j,-1,0) for l in 1:length(nottime)]...),i,i+CartesianIndex([ifelse(l==j,1,0) for l in 1:length(nottime)]...)]
53-
derivars = [[dot(left_weights(j),[depvars[j][central_neighbor_idxs(CartesianIndex(2),1)[1:2][2]],depvars[j][central_neighbor_idxs(CartesianIndex(2),1)[1:2][1]]]),
54-
dot(right_weights(j),[depvars[j][central_neighbor_idxs(CartesianIndex(length(space[1])-1),1)[end-1:end][1]],depvars[j][central_neighbor_idxs(CartesianIndex(length(space[1])-1),1)[end-1:end][2]]])]
55-
for j in 1:length(pdesys.depvars)]
56-
depvarderivmaps = reduce(vcat,[substitute.((Differential(nottime[j])(pdesys.depvars[i]),),edgevals) .=> derivars[i]
57-
for i in 1:length(pdesys.depvars) for j in 1:length(nottime)])
59+
left_idxs = central_neighbor_idxs(CartesianIndex(2),1)[1:2]
60+
right_idxs(j) = central_neighbor_idxs(CartesianIndex(length(space[j])-1),1)[end-1:end]
61+
# Constructs symbolic spatially discretized terms of the form e.g. au₂ - bu₁
62+
derivars = [[dot(left_weights(j),depvar[left_idxs]), dot(right_weights(j),depvar[right_idxs(j)])]
63+
for (j, depvar) in enumerate(depvars)]
64+
# Create list of all the symbolic Differential terms evaluated at boundary e.g. Differential(x)(u(t,0))
65+
subderivar(depvar,s) = substitute.((Differential(s)(depvar),),edgevals)
66+
# Create map of symbolic Differential terms with symbolic spatially discretized terms
67+
depvarderivmaps = reduce(vcat,[subderivar(depvar, s) .=> derivars[i]
68+
for (i, depvar) in enumerate(pdesys.depvars) for s in nottime])
5869
else
59-
# TODO: Fix Neumann and Robin on higher dimension
60-
depvarderivmaps = []
70+
# Higher order system
71+
# TODO: Fix Neumann and Robin on higher dimension
72+
depvarderivmaps = []
6173
end
6274

6375
# Generate initial conditions and bc equations
@@ -72,9 +84,11 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
7284
# Algebraic equations for BCs
7385
i = findfirst(x->occursin(x.val,bc.lhs),first.(depvarmaps))
7486

87+
# Replace Differential terms in the bc lhs with the symbolic spatially discretized terms
7588
# TODO: Fix Neumann and Robin on higher dimension
7689
lhs = length(nottime) == 1 ? substitute(bc.lhs,depvarderivmaps[i]) : bc.lhs
77-
90+
91+
# Replace symbol in the bc lhs with the spatial discretized term
7892
lhs = substitute(lhs,depvarmaps[i])
7993
rhs = substitute.((bc.rhs,),edgemaps[i])
8094
lhs = lhs isa Vector ? lhs : [lhs] # handle 1D
@@ -84,7 +98,7 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
8498
u0 = reduce(vcat,u0)
8599
bceqs = reduce(vcat,bceqs)
86100

87-
# Generate PDE Equations
101+
### PDE EQUATIONS ###
88102
interior = indices[[2:length(s)-1 for s in space]...]
89103
eqs = vec(map(Base.product(interior,pdeeqs)) do p
90104
i,eq = p
@@ -93,10 +107,11 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
93107
# on discretization.centered_order
94108
# TODO: Generalize central difference handling to allow higher even order derivatives
95109
central_neighbor_idxs(i,j) = [i+CartesianIndex([ifelse(l==j,-1,0) for l in 1:length(nottime)]...),i,i+CartesianIndex([ifelse(l==j,1,0) for l in 1:length(nottime)]...)]
96-
central_weights(i,j) = DiffEqOperators.calculate_weights(2, 0.0, [space[j][i[j]-1],space[j][i[j]],space[j][i[j]+1]])
97-
central_deriv_rules = [(Differential(nottime[j])^2)(pdesys.depvars[k]) => dot(central_weights(i,j),depvars[k][central_neighbor_idxs(i,j)]) for j in 1:length(nottime), k in 1:length(pdesys.depvars)]
110+
central_weights(i,j) = DiffEqOperators.calculate_weights(2, 0.0, space[j][i[j]-1:i[j]+1])
111+
central_deriv(i,j,k) = dot(central_weights(i,j),depvars[k][central_neighbor_idxs(i,j)])
112+
central_deriv_rules = [(Differential(s)^2)(pdesys.depvars[k]) => central_deriv(i,j,k) for (j,s) in enumerate(nottime), k in 1:length(pdesys.depvars)]
98113
valrules = vcat([pdesys.depvars[k] => depvars[k][i] for k in 1:length(pdesys.depvars)],
99-
[nottime[k] => space[k][i[k]] for k in 1:length(nottime)])
114+
[nottime[j] => space[j][i[j]] for j in 1:length(nottime)])
100115

101116
# TODO: Use rule matching for nonlinear Laplacian
102117

@@ -108,12 +123,14 @@ function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discret
108123
# *(~~a..., ~~b..., dot(forward_weights(i,j),depvars[k][central_neighbor_idxs(i,j)[2:3]]))))
109124
# for j in 1:length(nottime), k in 1:length(pdesys.depvars)]
110125

111-
substitute(eq.lhs,vcat(vec(central_deriv_rules),valrules)) ~ substitute(eq.rhs,vcat(vec(central_deriv_rules),valrules))
126+
rules = vcat(vec(central_deriv_rules),valrules)
127+
substitute(eq.lhs,rules) ~ substitute(eq.rhs,rules)
112128
end)
113129

114130
# Finalize
115131
defaults = pdesys.ps === nothing || pdesys.ps === SciMLBase.NullParameters() ? u0 : vcat(u0,pdesys.ps)
116132
ps = pdesys.ps === nothing || pdesys.ps === SciMLBase.NullParameters() ? Num[] : first.(pdesys.ps)
133+
# Combine PDE equations and BC equations
117134
sys = ODESystem(vcat(eqs,unique(bceqs)),t,vec(reduce(vcat,vec(depvars))),ps,defaults=Dict(defaults))
118135
sys, tspan
119136
end

test/MOL/MOL_1D_Linear_Diffusion.jl

Lines changed: 2 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,6 @@ end
172172

173173
# Solve ODE problem
174174
sol = solve(prob,Tsit5(),saveat=0.1)
175-
x = prob.space[2]
176-
t = sol.t
177175

178176
x = dx[2:end-1]
179177
t = sol.t
@@ -265,159 +263,15 @@ end
265263

266264
# Solve ODE problem
267265
sol = solve(prob,Tsit5(),saveat=0.1)
268-
x = dx[2:end-1]
269-
t = sol.t
270-
271-
# Test against exact solution
272-
for i in 1:length(sol)
273-
exact = u_exact(x, t[i])
274-
u_approx = sol.u[i]
275-
@test u_approx exact atol=0.01
276-
end
277-
end
278-
279-
@testset "Test 06: Dt(u(t,x)) ~ Dxx(u(t,x)), time-dependent Robin BCs" begin
280-
# Method of Manufactured Solutions
281-
u_exact = (x,t) -> exp.(-t) * sin.(x)
282-
283-
# Parameters, variables, and derivatives
284-
@parameters t x
285-
@variables u(..)
286-
Dt = Differential(t)
287-
Dx = Differential(x)
288-
Dxx = Differential(x)^2
289-
290-
# 1D PDE and boundary conditions
291-
eq = Dt(u(t,x)) ~ Dxx(u(t,x))
292-
bcs = [u(0,x) ~ sin(x),
293-
t^2 * u(t,-1.0) + 3Dx(u(t,-1.0)) ~ exp(-t) * (t^2 * sin(-1.0) + 3cos(-1.0)),
294-
4u(t,1.0) + t * Dx(u(t,1.0)) ~ exp(-t) * (4sin(1.0) + t * cos(1.0))]
295-
296-
# Space and time domains
297-
domains = [t IntervalDomain(0.0,1.0),
298-
x IntervalDomain(-1.0,1.0)]
299-
300-
# PDE system
301-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
302-
303-
# Method of lines discretization
304-
dx = 0.01
305-
order = 2
306-
discretization = MOLFiniteDifference([x=>dx],t)
307-
308-
# Convert the PDE problem into an ODE problem
309-
prob = discretize(pdesys,discretization)
310-
311-
# Solve ODE problem
312-
sol = solve(prob,Tsit5(),saveat=0.1)
313-
x = prob.space[2]
314-
t = sol.t
315-
316-
x = dx[2:end-1]
266+
x = (-1:dx:1)[2:end-1]
317267
t = sol.t
318268

319269
# Test against exact solution
320270
for i in 1:length(sol)
321271
exact = u_exact(x, t[i])
322272
u_approx = sol.u[i]
323-
@test u_approx exact atol=0.01
273+
@test u_approx exact atol=0.1
324274
end
325275
end
326276

327-
@testset "Test errors" begin
328-
# Parameters, variables, and derivatives
329-
@parameters t x
330-
@variables u(..) v(..)
331-
Dt = Differential(t)
332-
Dx = Differential(x)
333-
Dxx = Differential(x)^2
334-
335-
# 1D PDE and boundary conditions
336-
eq = Dt(u(t,x)) ~ Dxx(u(t,x))
337-
# Space and time domains
338-
domains = [t IntervalDomain(0.0,1.0), x IntervalDomain(0.0,1.0)]
339-
340-
# Method of lines discretization
341-
dx = 0.1
342-
order = 2
343-
discretization = MOLFiniteDifference([x=>dx],t)
344-
345-
# Missing boundary condition
346-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
347-
Dx(u(t,1)) ~ 0.0]
348-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
349-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
350-
351-
# Boundary condition not at t=0
352-
bcs = [u(1,x) ~ 0.5 + sin(2pi*x),
353-
Dx(u(t,0)) ~ 0.0,
354-
Dx(u(t,1)) ~ 0.0]
355-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
356-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
357277

358-
# Boundary condition not at an edge of the domain
359-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
360-
Dx(u(t,0)) ~ 0.0,
361-
Dx(u(t,0.5)) ~ 0.0]
362-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
363-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
364-
365-
# Second-order derivative in BC
366-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
367-
Dxx(u(t,0)) ~ 0.0,
368-
Dx(u(t,0.5)) ~ 0.0]
369-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
370-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
371-
372-
# Wrong format for Robin BCs
373-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
374-
Dx(u(t,0)) ~ 0.0,
375-
u(t,1) * Dx(u(t,1)) ~ 0.0]
376-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
377-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
378-
379-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
380-
Dx(u(t,0)) ~ 0.0,
381-
u(t,1) + Dxx(u(t,1)) ~ 0.0]
382-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
383-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
384-
385-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
386-
Dx(u(t,0)) ~ 0.0,
387-
u(t,1) + 2Dxx(u(t,1)) ~ 0.0]
388-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
389-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
390-
391-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
392-
Dx(u(t,0)) ~ 0.0,
393-
Dx(u(t,1)) + u(t,1) ~ 0.0]
394-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
395-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
396-
397-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
398-
Dx(u(t,0)) ~ 0.0,
399-
u(t,1) / 2 + Dx(u(t,1)) ~ 0.0]
400-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
401-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
402-
403-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
404-
Dx(u(t,0)) ~ 0.0,
405-
u(t,1) + Dx(u(t,1)) / 2 ~ 0.0]
406-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
407-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
408-
409-
# Mismatching arguments
410-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
411-
Dx(u(t,0)) ~ 0.0,
412-
u(t,0) + Dx(u(t,1)) ~ 0.0]
413-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
414-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
415-
416-
# Mismatching variables
417-
bcs = [u(0,x) ~ 0.5 + sin(2pi*x),
418-
Dx(u(t,0)) ~ 0.0,
419-
u(t,1) + Dx(v(t,1)) ~ 0.0]
420-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
421-
@test_throws BoundaryConditionError discretize(pdesys,discretization)
422-
423-
end

0 commit comments

Comments
 (0)