Skip to content

Commit b8fd254

Browse files
committed
Fixed interface, rules, bcs
1 parent e02bee7 commit b8fd254

File tree

10 files changed

+176
-101
lines changed

10 files changed

+176
-101
lines changed

src/discretization/MOL_discretization.jl renamed to src/MOL_discretization.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function SciMLBase.symbolic_discretize(pdesys::PDESystem, discretization::Method
5454
s = DiscreteSpace(domain, depvars, indvars, x̄, discretization)
5555

5656
# Get the finite difference weights
57-
derivweights = DifferentialDiscretizer(pde, s, discretization)
57+
derivweights = DifferentialDiscretizer(pde, pdesys.bcs, s, discretization)
5858

5959
# Get the boundary conditions
6060
BoundaryHandler!!(u0, bceqs, pdesys.bcs, s, depvar_ops, tspan, derivweights)

src/MOL_utils.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ function split_additive_terms(eq)
8181
return vcat(lhs_arg,rhs_arg)
8282
end
8383

84-
function clip(I::CartesianIndex, s::DiscreteSpace{N}, j::Int) where N
85-
# Clip the index by 1 at each end of the dimension
84+
@inline function clip(I::CartesianIndex, s::DiscreteSpace{N}, j::Int, bpc::Int) where N
8685
I1 = unitindices(N)[j]
87-
return I[j] > length(s, j) ? I-I1 : I+I1
86+
return I-I1
87+
8888
end
8989

9090
subsmatch(expr, rule) = isequal(substitute(expr, rule), expr) ? false : true

src/MethodOfLines.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,21 @@ module MethodOfLines
99
using SymbolicUtils: operation, arguments
1010
import DomainSets
1111

12-
include("MOL_utils.jl")
1312
include("discretization/fornberg.jl")
1413

1514
include("grid_types.jl")
1615
include("MOLFiniteDifference.jl")
1716
include("bcs/boundary_types.jl")
18-
17+
1918
include("discretization/discretize_vars.jl")
19+
include("MOL_utils.jl")
2020

2121
include("discretization/differential_discretizer.jl")
2222
include("discretization/generate_finite_difference_rules.jl")
2323

2424
include("bcs/generate_bc_eqs.jl")
2525

26-
include("discretization/MOL_discretization.jl")
26+
include("MOL_discretization.jl")
2727

2828
export MOLFiniteDifference, discretize, symbolic_discretize, grid_align, edge_align, center_align
2929
end

src/bcs/generate_bc_eqs.jl

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ function _boundary_rules(s, orders, x, val)
44

55
args = substitute.(args, (x=>val,))
66

7-
rules = [operation(u)(args...) => (operation(u)(args...), x) for u in ū]
7+
rules = [operation(u)(args...) => (operation(u)(args...), x) for u in s.ū]
88

9-
return vcat(rules, vec([(Differential(x)^d)(operation(u)(args...)) => (operation(u)(args...), x) for d in orders[x], u in ū]))
9+
return vcat(rules, vec([(Differential(x)^d)(operation(u)(args...)) => (operation(u)(args...), x) for d in orders[x], u in s.ū]))
1010
end
1111

1212
function generate_boundary_matching_rules(s, orders)
@@ -31,9 +31,9 @@ function BoundaryHandler!!(u0, bceqs, bcs, s::DiscreteSpace, depvar_ops, tspan,
3131
t=s.time
3232

3333
if t === nothing
34-
initmaps =
34+
initmaps = s.
3535
else
36-
initmaps = substitute.(ū,[t=>tspan[1]])
36+
initmaps = substitute.(s.ū,[t=>tspan[1]])
3737
end
3838

3939
# Create some rules to match which bundary/variable a bc concerns
@@ -52,13 +52,13 @@ function BoundaryHandler!!(u0, bceqs, bcs, s::DiscreteSpace, depvar_ops, tspan,
5252
# * Assume in the form `u(...) ~ ...` for now
5353
bcdepvar = first(get_depvars(bc.lhs, depvar_ops))
5454

55-
if any(u -> isequal(operation(u), operation(bcdepvar)), ū)
55+
if any(u -> isequal(operation(u), operation(bcdepvar)), s.ū)
5656
if t !== nothing && operation(bc.lhs) isa Sym && !any(x -> isequal(x, t.val), arguments(bc.lhs))
5757
# initial condition
5858
# * Assume that the initial condition is not in terms of the initial derivative
5959
initindex = findfirst(isequal(bc.lhs), initmaps)
6060
if initindex !== nothing
61-
push!(u0,vec(s.discvars[ū[initindex]] .=> substitute.((bc.rhs,),gridvals(s))))
61+
push!(u0,vec(s.discvars[s.ū[initindex]] .=> substitute.((bc.rhs,),gridvals(s))))
6262
end
6363
else
6464
# Split out additive terms
@@ -94,7 +94,7 @@ function BoundaryHandler!!(u0, bceqs, bcs, s::DiscreteSpace, depvar_ops, tspan,
9494
end
9595

9696
@assert boundary !== nothing "Boundary condition $bc is not on a boundary of the domain, or is not a valid boundary condition"
97-
97+
@show bc
9898
push!(bceqs, vec(map(s.Iedge[x_][idx(boundary)]) do II
9999
rules = generate_bc_rules(II, derivweights, s, bc, u_, x_, boundary)
100100

@@ -112,9 +112,9 @@ function generate_bc_rules(II, derivweights, s::DiscreteSpace{N,M,G}, bc, u_, x_
112112

113113
depvarderivbcmaps = []
114114
depvarbcmaps = []
115-
115+
116116
# * Assume that the BC is in terms of an explicit expression, not containing references to variables other than u_ at the boundary
117-
for u in
117+
for u in s.
118118
if isequal(operation(u), operation(u_))
119119
# What to replace derivatives at the boundary with
120120
depvarderivbcmaps = [(Differential(x_)^d)(u_) => central_difference(derivweights.map[Differential(x_)^d], II, s, (s.x2i[x_], x_), u, ufunc) for d in derivweights.orders[x_]]
@@ -123,7 +123,7 @@ function generate_bc_rules(II, derivweights, s::DiscreteSpace{N,M,G}, bc, u_, x_
123123
break
124124
end
125125
end
126-
126+
@show depvarderivbcmaps
127127
fd_rules = generate_finite_difference_rules(II, s, bc, derivweights)
128128
varrules = axiesvals(s, x_, II)
129129

@@ -143,7 +143,8 @@ function generate_bc_rules(II, derivweights, s::DiscreteSpace{N,M,G}, bc, u_, x_
143143
# depvarbcmaps will dictate what to replace the variable terms with in the bcs
144144
# replace u(t,0) with u₁, etc
145145
# * Assume that the BC is in terms of an explicit expression, not containing references to variables other than u_ at the boundary
146-
for u in
146+
147+
for u in s.
147148
if isequal(operation(u), operation(u_))
148149
depvarderivbcmaps = [(Differential(x_)^d)(u_) => half_offset_centered_difference(derivweights.halfoffsetmap[Differential(x_)^d], II, s, (s.x2i[x_],x_), u, ufunc) for d in derivweights.orders[x_]]
149150

src/discretization/differential_discretizer.jl

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,28 @@ struct DifferentialDiscretizer{T, D1}
77
orders::Dict{Num, Vector{Int}}
88
end
99

10-
function DifferentialDiscretizer(pde, s, discretization)
10+
function DifferentialDiscretizer(pde, bcs, s, discretization)
1111
approx_order = discretization.approx_order
1212
# TODO: Include bcs in this calculation
13-
d_orders(x) = reverse(sort(collect(union(differential_order(pde.rhs, x), differential_order(pde.lhs, x)))))
13+
d_orders(x) = reverse(sort(collect(union(differential_order(pde.rhs, x), differential_order(pde.lhs, x), (differential_order(bc.rhs, x) for bc in bcs)..., (differential_order(bc.lhs, x) for bc in bcs)...))))
1414

15-
# central_deriv_rules = [(Differential(s)^2)(u) => central_deriv(2,II,j,k) for (j,s) in enumerate(s.x̄), (k,u) in enumerate(ū)]
15+
# central_deriv_rules = [(Differential(s)^2)(u) => central_deriv(2,II,j,k) for (j,s) in enumerate(s.x̄), (k,u) in enumerate(s.ū)]
1616
differentialmap = Array{Pair{Num,DiffEqOperators.DerivativeOperator},1}()
1717
nonlinlap = []
1818
interp = []
1919
orders = []
2020
# Hardcoded to centered difference, generate weights for each differential
2121
# TODO: Add handling for upwinding
22+
2223
for x in s.
23-
push!(orders, x => d_orders(x))
24+
orders_ = d_orders(x)
25+
push!(orders, x => orders_)
26+
_orders = Set(vcat(orders_, [1,2]))
2427
# TODO: Only generate weights for derivatives that are actually used and avoid redundant calculations
25-
rs = [(Differential(x)^d) => CompleteCenteredDifference(d, approx_order, s.dxs[x] ) for d in last(orders).second]
28+
rs = [(Differential(x)^d) => CompleteCenteredDifference(d, approx_order, s.dxs[x] ) for d in _orders]
29+
2630

27-
nonlinlap = vcat(nonlinlap, [Differential(x)^d => CompleteHalfCenteredDifference(d, approx_order, s.dxs[x]) for d in last(orders).second])
31+
nonlinlap = vcat(nonlinlap, [Differential(x)^d => CompleteHalfCenteredDifference(d, approx_order, s.dxs[x]) for d in _orders])
2832
differentialmap = vcat(differentialmap, rs)
2933
# A 0th order derivative off the grid is an interpolation
3034
push!(interp, x => CompleteHalfCenteredDifference(0, approx_order, s.dxs[x]))
@@ -92,7 +96,7 @@ end
9296

9397
# i is the index of the offset, assuming that there is one precalculated set of weights for each offset required for a first order finite difference
9498
function half_offset_centered_difference(D, II, s, jx, u, ufunc)
95-
96-
(weights, Itap) = get_half_offset_weights_and_stencil(D, II, s, jx)
99+
j, x = jx
100+
weights, Itap = get_half_offset_weights_and_stencil(D, II, s, jx)
97101
return dot(weights, ufunc(u, Itap, x))
98102
end

src/discretization/discretize_vars.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ end
1717
return substitute.((depvar,), edge_vals)
1818
end
1919
struct DiscreteSpace{N,M,G}
20-
vars
20+
2121
discvars
2222
time
2323
# Note that these aren't necessarily @parameters
@@ -35,6 +35,7 @@ nparams(::DiscreteSpace{N,M}) where {N,M} = N
3535
nvars(::DiscreteSpace{N,M}) where {N,M} = M
3636

3737
Base.length(s::DiscreteSpace, x) = length(s.grid[x])
38+
Base.length(s::DiscreteSpace, j::Int) = length(s.grid[s.x̄[j]])
3839
Base.size(s::DiscreteSpace) = Tuple(length(s.grid[z]) for z in s.x̄)
3940

4041
params(s::DiscreteSpace{N,M}) where {N,M}= s.params
@@ -57,7 +58,7 @@ gridvals(s::DiscreteSpace{N}, I::CartesianIndex) where N = [Pair(x, s.grid[x][I[
5758

5859
## Boundary methods ##
5960
edgevals(s::DiscreteSpace{N}) where {N} = reduce(vcat, [get_edgevals(s.x̄, s.axies, i) for i = 1:N])
60-
edgevars(s::DiscreteSpace, I) = [u => s.discvars[u][I] for u in ū]
61+
edgevars(s::DiscreteSpace, I) = [u => s.discvars[u][I] for u in s.ū]
6162

6263
"""
6364
Generate a map of variables to the gridpoints at the edge of the domain
@@ -69,15 +70,15 @@ end
6970
return [x => last(s.axies[x]) for x in s.x̄]
7071
end
7172

72-
varmaps(s::DiscreteSpace, II) = [u => s.discvars[u][II] for u in ū]
73+
varmaps(s::DiscreteSpace, II) = [u => s.discvars[u][II] for u in s.ū]
7374

7475
valmaps(s::DiscreteSpace, II) = vcat(varmaps(s,II), gridvals(s,II))
7576

7677

7778

7879
Iinterior(s::DiscreteSpace) = s.Igrid[[2:(length(s, x)-1) for x in s.x̄]...]
7980

80-
map_symbolic_to_discrete(II::CartesianIndex, s::DiscreteSpace{N,M}) where {N,M} = vcat([ū[k] => s.discvars[k][II] for k = 1:M], [s.x̄[j] => s.grid[j][II[j]] for j = 1:N])
81+
map_symbolic_to_discrete(II::CartesianIndex, s::DiscreteSpace{N,M}) where {N,M} = vcat([s.ū[k] => s.discvars[k][II] for k = 1:M], [s.x̄[j] => s.grid[j][II[j]] for j = 1:N])
8182

8283
# ? How rude is this? Makes Iedge work
8384

@@ -141,5 +142,5 @@ end
141142

142143

143144

144-
#varmap(s::DiscreteSpace{N,M}) where {N,M} = [ū[i] => i for i = 1:M]
145+
#varmap(s::DiscreteSpace{N,M}) where {N,M} = [s.ū[i] => i for i = 1:M]
145146

src/discretization/generate_finite_difference_rules.jl

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,8 @@
33
44
Interpolate gridpoints by taking the average of the values of the discrete points, or if the offset is outside the grid, extrapolate the value with dx.
55
"""
6-
function interpolate_discrete_param(II, s, itap, j, x)
7-
# * This will need to be updated to dispatch on grid type when grids become more general
8-
if (II[j]+itap) < 1
9-
return s.grid[x][1]+s.dxs[x]*offset
10-
elseif (II[j]+itap) > (length(x) - 1)
11-
return s.grid[x][length(x)]+s.dxs[x]*offset
12-
else
13-
return (s.grid[x][II[j]+itap]+s.grid[x][II[j]+itap+1])/2
14-
end
6+
@inline function interpolate_discrete_param(i, s, itap, x, bpc)
7+
return s.grid[x][i+itap]+s.dxs[x]*.5
158
end
169

1710
"""
@@ -44,35 +37,38 @@ And so on.
4437
function cartesian_nonlinear_laplacian(expr, II, derivweights, s, x, u)
4538
# Based on the paper https://web.mit.edu/braatzgroup/analysis_of_finite_difference_discretization_schemes_for_diffusion_in_spheres_with_variable_diffusivity.pdf
4639
# See scheme 1, namely the term without the 1/r dependence. See also #354 and #371 in DiffEqOperators, the previous home of this package.
40+
41+
jx = j, x = (s.x2i[x], x)
42+
@assert II[j] != 1 "The nonlinear laplacian is only defined on the interior of the grid, it is unsupported in boundary conditions."
43+
@assert II[j] != length(s, x) "The nonlinear laplacian is only defined on the interior of the grid, it is unsupported in boundary conditions."
4744

48-
jx = j, x = (s.x2i(x), x)
4945
D_inner = derivweights.halfoffsetmap[Differential(x)]
5046
inner_interpolater = derivweights.interpmap[x]
5147

52-
# Get the outer weights and stencil.
53-
outerweights, outerstencil = _get_weights_and_stencil(D_inner, clip(II, s, j), s, jx)
48+
# Get the outer weights and stencil. clip() essentially removes a point from either end of the grid, for this reason this function is only defined on the interior, not in bcs
49+
outerweights, outerstencil = get_half_offset_weights_and_stencil(D_inner, clip(II, s, j, D_inner.boundary_point_count), s, jx)
5450

5551
# Get the correct weights and stencils for this II
5652
inner_deriv_weights_and_stencil = [get_half_offset_weights_and_stencil(D_inner, I, s, jx) for I in outerstencil]
5753
interp_weights_and_stencil = [get_half_offset_weights_and_stencil(inner_interpolater, I, s, jx) for I in outerstencil]
5854

5955
# map variables to symbolically inerpolated/extrapolated expressions
60-
map_vars_to_interpolated(stencil, weights) = [v => dot(weights, s.discvars[v][stencil]) for v in ū]
56+
map_vars_to_interpolated(stencil, weights) = [v => dot(weights, s.discvars[v][stencil]) for v in s.ū]
6157

6258
# Map parameters to interpolated values. Using simplistic extrapolation/interpolation for now as grids are uniform
63-
map_params_to_interpolated(itap) = [z => interpolate_discrete_param(II, s, itap, i, z) for (i,z) in enumerate(s.x̄) ]
59+
map_params_to_interpolated(itap) = x => interpolate_discrete_param(II[j], s, itap[j]-II[j], x, D_inner.boundary_point_count)
6460

6561
# Take the inner finite difference
6662
inner_difference = [dot(inner_weights, s.discvars[u][inner_stencil]) for (inner_weights, inner_stencil) in inner_deriv_weights_and_stencil]
6763

6864
# Symbolically interpolate the multiplying expression
69-
interpolated_expr = [Num(substitute(substitute(expr, map_vars_to_interpolated(interpstencil, interpweights)), map_params_to_interpolated(itap)))
70-
for (itap,(interpweights, interpstencil)) in zip(itaps, interp_weights_and_stencil)]
65+
interpolated_expr = [Num(substitute(substitute(expr, map_vars_to_interpolated(interpstencil, interpweights)), map_params_to_interpolated.(interpstencil)))
66+
for (interpweights, interpstencil) in interp_weights_and_stencil]
7167

7268
# multiply the inner finite difference by the interpolated expression, and finally take the outer finite difference
7369
return dot(outerweights, inner_difference .* interpolated_expr)
7470
end
75-
71+
7672
"""
7773
`cartesian_nonlinear_laplacian`
7874
@@ -106,16 +102,16 @@ function spherical_diffusion(innerexpr, II, derivweights, s, r, u)
106102
D_2 = derivweights.map[Differential(r)^2]
107103

108104
# What to replace parameter x with given I
109-
_rsubs(x, I) = x => s.grid[x][I[s.x2i(x)]]
105+
_rsubs(x, I) = x => s.grid[x][I[s.x2i[x]]]
110106
# Full rules for substituting parameters in the inner expression
111-
rsubs(I) = vcat([v => s.discvars[v][I] for v in ū], [_rsubs(x, I) for x in params(s)])
107+
rsubs(I) = vcat([v => s.discvars[v][I] for v in s.ū], [_rsubs(x, I) for x in s.])
112108
# Discretization func for u
113109
ufunc_u(v, I, x) = s.discvars[v][I]
114110

115111
# 2nd order finite difference in u
116112
exprhere = Num(substitute(innerexpr, rsubs(II)))
117113
# Catch the r ≈ 0 case
118-
if substitute(r, _rsubs(r, II)) 0
114+
if Symbolics.unwrap(substitute(r, _rsubs(r, II))) 0
119115
D_2_u = central_difference(D_2, II, s, (s.x2i(r), r), u, ufunc_u)
120116
return 3exprhere*D_2_u # See appendix B of the paper
121117
end
@@ -127,24 +123,24 @@ end
127123

128124
@inline function generate_cartesian_rules(II, s, derivweights, terms)
129125
central_ufunc(u, I, x) = s.discvars[u][I]
130-
return reduce(vcat, [[(Differential(x)^d)(u) => central_difference(derivweights.map[Differential(x)^d], II, s, (j,x), u, central_ufunc) for d in derivweights.orders[x], u in ū] for (j,x) in enumerate(s.x̄)])
126+
return reduce(vcat, [[(Differential(x)^d)(u) => central_difference(derivweights.map[Differential(x)^d], II, s, (j,x), u, central_ufunc) for d in derivweights.orders[x], u in s.ū] for (j,x) in enumerate(s.x̄)])
131127
end
132128

133129
@inline function generate_upwinding_rules(II, s, derivweights, terms)
134130
#forward_weights(II,j) = calculate_weights(discretization.upwind_order, 0.0, s.grid[j][[II[j],II[j]+1]])
135131
#reverse_weights(II,j) = calculate_weights(discretization.upwind_order, 0.0, s.grid[j][[II[j]-1,II[j]]])
136132
# upwinding_rules = [@rule(*(~~a, $(Differential(s.x̄[j]))(u),~~b) => IfElse.ifelse(*(~~a..., ~~b...,)>0,
137-
# *(~~a..., ~~b..., dot(reverse_weights(II,j),ū[k][central_neighbor_idxs(II,j)[1:2]])),
138-
# *(~~a..., ~~b..., dot(forward_weights(II,j),ū[k][central_neighbor_idxs(II,j)[2:3]]))))
139-
# for j in 1:nparams(s), k in 1:length(pdesys.ū)]
133+
# *(~~a..., ~~b..., dot(reverse_weights(II,j),s.ū[k][central_neighbor_idxs(II,j)[1:2]])),
134+
# *(~~a..., ~~b..., dot(forward_weights(II,j),s.ū[k][central_neighbor_idxs(II,j)[2:3]]))))
135+
# for j in 1:nparams(s), k in 1:length(pdesys.s.ū)]
140136

141137
end
142138

143139
@inline function generate_nonlinlap_rules(II, s, derivweights, terms)
144-
cartesian_deriv_rules = [@rule ($(Differential(x))(*(~~a, $(Differential(x))(u), ~~b))) => cartesian_nonlinear_laplacian(*(a..., b...), II, derivweights, s, x, u) for x in s.x̄, u in ū]
140+
cartesian_deriv_rules = [@rule *(~~c, $(Differential(x))(*(~~a, $(Differential(x))(u), ~~b)), ~~d) => cartesian_nonlinear_laplacian(*(a..., b...), II, derivweights, s, x, u) for x in s.x̄, u in s.ū]
145141

146142
cartesian_deriv_rules = vcat(vec(cartesian_deriv_rules),vec(
147-
[@rule ($(Differential(x))($(Differential(x))(u)/~a)) => cartesian_nonlinear_laplacian(1/~a, II, derivweights, s, x, u) for x in s.x̄, u in ū]))
143+
[@rule ($(Differential(x))($(Differential(x))(u)/~a)) => cartesian_nonlinear_laplacian(1/~a, II, derivweights, s, x, u) for x in s.x̄, u in s.ū]))
148144

149145
nonlinlap_rules = []
150146
for t in terms
@@ -158,12 +154,14 @@ end
158154
end
159155

160156
@inline function generate_spherical_diffusion_rules(II, s, derivweights, terms)
161-
spherical_deriv_rules = vec([@rule *(~~a, (r^-2), ($(Differential(r))(*(~~c, (r^2), ~~d, $(Differential(r))(u), ~~e))), ~~b) => *(~a..., spherical_diffusion(*(~c..., ~d..., ~e...), II, derivweights, s, r, u), ~b...)
162-
for r in s.x̄, u in ū])
157+
rules = vec([@rule *(~~a, 1/(r^2), ($(Differential(r))(*(~~c, (r^2), ~~d, $(Differential(r))(u), ~~e))), ~~b) => *(~a..., spherical_diffusion(*(~c..., ~d..., ~e...), II, derivweights, s, r, u), ~b...)
158+
for r in s.x̄, u in s.ū])
159+
rules = vcat(rules, vec([@rule /(($(Differential(r))(*(~~c, (r^2), ~~d, $(Differential(r))(u), ~~e))), (r^2)) => spherical_diffusion(*(~c..., ~d..., ~e...), II, derivweights, s, r, u)
160+
for r in s.x̄, u in s.ū]))
163161

164162
spherical_diffusion_rules = []
165163
for t in terms
166-
for r in spherical_deriv_rules
164+
for r in rules
167165
if r(t) !== nothing
168166
push!(spherical_diffusion_rules, t => r(t))
169167
end

0 commit comments

Comments
 (0)