Skip to content

Commit e870cf6

Browse files
Merge pull request #17 from xtalax/arbitrary-approx-order
Arbitrary approx order
2 parents 5a387e6 + 4cbf4b2 commit e870cf6

28 files changed

+1494
-709
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@
44
Manifest.toml
55
.vscode
66
.vscode/*
7+
plots
8+
plots/*
9+
src/scratch
10+
src/scratch/*
11+
test/plots
12+
test/plots/*

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
1818
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1919

2020
[compat]
21-
DiffEqOperators = "4"
21+
DiffEqOperators = "4.40.0"
2222
DomainSets = "0.5"
2323
ModelingToolkit = "8"
2424
NonlinearSolve = "0.3"

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,24 @@ Feature requests and issues welcome.
99
discretization = MOLFiniteDifference(dxs,
1010
<your choice of continuous variable, usually time>;
1111
upwind_order = <currently hard coded to 1>,
12-
centered_order = <currently hard coded to 2>,
12+
approx_order = Order of derivative approximation, starting from 2
1313
grid_align = your grid type choice>)
1414
prob = discretize(pdesys, discretization)
1515
```
1616
Where `dxs` is a vector of pairs of parameters to the grid step in this dimension, i.e. `[x=>0.2, y=>0.1]`
1717

1818
Note that the second argument to `MOLFiniteDifference` is optional, all parameters can be discretized if all boundary conditions are specified
1919

20-
Currently supported grid types: `center_align` and `edge_align`
20+
Currently supported grid types: `center_align` and `edge_align`. Edge align will give better accuracy with Neumann Boundary conditions.
2121

2222
`center_align`: naive grid, starting from lower boundary, ending on upper boundary with step of `dx`
2323

2424
`edge_align`: offset grid, set halfway between the points that would be generated with center_align, with extra points at either end that are above and below the supremum and infimum by `dx/2`.
2525

26-
Currently boundary conditions defined in terms of derivatives at the boundary are unsupported above 1 discretized dimension. Periodic conditions are also unsupported.
26+
Currently Periodic boundary conditions are unsupported
2727

2828
## Coming soon:
29-
- Arbitrary approximation order
29+
- Automatic up/downwinding for odd order derivatives
3030
- Above mentioned unsupported boundary conditions supported
3131

3232
## Full Example:

src/MOLFiniteDifference.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
struct MOLFiniteDifference{G} <: DiffEqBase.AbstractDiscretization
2+
dxs
3+
time
4+
approx_order::Int
5+
upwind_order::Int
6+
grid_align::G
7+
end
8+
9+
# Constructors. If no order is specified, both upwind and centered differences will be 2nd order
10+
function MOLFiniteDifference(dxs, time=nothing; approx_order = 2, upwind_order = 1, grid_align=CenterAlignedGrid())
11+
12+
if approx_order % 2 != 0
13+
@warn "Discretization approx_order must be even, rounding up to $(approx_order+1)"
14+
end
15+
return MOLFiniteDifference{typeof(grid_align)}(dxs, time, approx_order, upwind_order, grid_align)
16+
end

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

Lines changed: 30 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,15 @@
11
# Method of lines discretization scheme
22

3-
@enum GridAlign center_align edge_align
4-
5-
struct MOLFiniteDifference{T,T2} <: DiffEqBase.AbstractDiscretization
6-
dxs::T
7-
time::T2
8-
upwind_order::Int
9-
centered_order::Int
10-
grid_align::GridAlign
11-
end
12-
13-
# Constructors. If no order is specified, both upwind and centered differences will be 2nd order
14-
MOLFiniteDifference(dxs, time=nothing; upwind_order = 1, centered_order = 2, grid_align=center_align) =
15-
MOLFiniteDifference(dxs, time, upwind_order, centered_order, grid_align)
16-
17-
function SciMLBase.symbolic_discretize(pdesys::PDESystem, discretization::MethodOfLines.MOLFiniteDifference)
3+
function SciMLBase.symbolic_discretize(pdesys::PDESystem, discretization::MethodOfLines.MOLFiniteDifference{G}) where G
184
pdeeqs = pdesys.eqs isa Vector ? pdesys.eqs : [pdesys.eqs]
195
bcs = pdesys.bcs
206
domain = pdesys.domain
21-
7+
228
grid_align = discretization.grid_align
239
t = discretization.time
2410
# Get tspan
2511
tspan = nothing
12+
# Check that inputs make sense
2613
if t !== nothing
2714
tdomain = pdesys.domain[findfirst(d->isequal(t.val, d.variables), pdesys.domain)]
2815
@assert tdomain.domain isa DomainSets.Interval
@@ -43,11 +30,12 @@ function SciMLBase.symbolic_discretize(pdesys::PDESystem, discretization::Method
4330
depvars_lhs = get_depvars(pde.lhs, depvar_ops)
4431
depvars_rhs = get_depvars(pde.rhs, depvar_ops)
4532
depvars = collect(depvars_lhs depvars_rhs)
33+
4634
# Read the independent variables,
4735
# ignore if the only argument is [t]
4836
allindvars = Set(filter(xs->!isequal(xs, [t]), map(arguments, depvars)))
49-
allnottime = Set(filter(!isempty, map(u->filter(x-> t === nothing || !isequal(x, t.val), arguments(u)), depvars)))
50-
if isempty(allnottime)
37+
allx̄ = Set(filter(!isempty, map(u->filter(x-> t === nothing || !isequal(x, t.val), arguments(u)), depvars)))
38+
if isempty(allx̄)
5139
push!(alleqs, pde)
5240
push!(alldepvarsdisc, depvars)
5341
for bc in bcs
@@ -57,59 +45,38 @@ function SciMLBase.symbolic_discretize(pdesys::PDESystem, discretization::Method
5745
end
5846
else
5947
# make sure there is only one set of independent variables per equation
60-
@assert length(allnottime) == 1
61-
nottime = first(allnottime)
48+
@assert length(allx̄) == 1
49+
= first(allx̄)
6250
@assert length(allindvars) == 1
6351
indvars = first(allindvars)
64-
nspace = length(nottime)
52+
#TODO: Factor this out of the loop, along with BCs
53+
# Get the grid
54+
s = DiscreteSpace(domain, depvars, indvars, x̄, discretization)
55+
56+
# Get the finite difference weights
57+
derivweights = DifferentialDiscretizer(pde, pdesys.bcs, s, discretization)
6558

66-
s = DiscreteSpace(pdesys.domain, depvars, indvars, nottime, grid_align, discretization)
59+
# Get the boundary conditions
60+
BoundaryHandler!!(u0, bceqs, pdesys.bcs, s, depvar_ops, tspan, derivweights)
6761

68-
#---- Count Boundary Equations --------------------
69-
# Count the number of boundary equations that lie at the spatial boundary on
70-
# both the left and right side. This will be used to determine number of
71-
# interior equations s.t. we have a balanced system of equations.
62+
# Find the indexes on the interior
7263

73-
# get the depvar boundary terms for given depvar and indvar index.
74-
get_depvarbcs(depvar, i) = substitute.((depvar,),get_edgevals(s, i))
75-
76-
# return the counts of the boundary-conditions that reference the "left" and
77-
# "right" edges of the given independent variable. Note that we return the
78-
# max of the count for each depvar in the system of equations.
79-
@inline function get_bc_counts(i)
80-
left = 0
81-
right = 0
82-
for depvar in s.vars
83-
depvaredges = get_depvarbcs(depvar, i)
84-
counts = [map(x->occursin(x, bc.lhs), depvaredges) for bc in pdesys.bcs]
85-
left = max(left, sum([c[1] for c in counts]))
86-
right = max(right, sum([c[2] for c in counts]))
87-
end
88-
return [left, right]
89-
end
90-
9164

92-
#TODO: Update this when allowing higher order derivatives to correctly deal with boundary upwinding
93-
interior = s.Igrid[[let bcs = get_bc_counts(i)
94-
(1 + first(bcs)):length(g)-last(bcs)
95-
end
96-
for (i,g) in enumerate(s.grid)]...]
97-
98-
### PDE EQUATIONS ###
99-
# Create a stencil in the required dimension centered around 0
100-
# e.g. (-1,0,1) for 2nd order, (-2,-1,0,1,2) for 4th order, etc
101-
if discretization.centered_order % 2 != 0
102-
throw(ArgumentError("Discretization centered_order must be even, given $(discretization.centered_order)"))
103-
end
104-
# For all cartesian indices in the interior, generate finite difference rules
105-
eqs = vec(map(interior) do II
106-
rules = generate_finite_difference_rules(II, s, pde, discretization)
65+
Iinterior = s.Igrid[[let bcs = get_bc_counts(i, s, pdesys.bcs)
66+
(1 + first(bcs)):length(s.grid[x])-last(bcs)
67+
end
68+
for (i,x) in enumerate(s.x̄)]...]
69+
70+
71+
72+
# Discretize the equation on the interior
73+
pdeeqs = vec(map(Iinterior) do II
74+
rules = vcat(generate_finite_difference_rules(II, s, pde, derivweights), valmaps(s, II))
10775
substitute(pde.lhs,rules) ~ substitute(pde.rhs,rules)
10876
end)
10977

110-
generate_u0_and_bceqs!!(u0, bceqs, pdesys.bcs, s, depvar_ops, tspan, discretization)
111-
push!(alleqs,eqs)
112-
push!(alldepvarsdisc, reduce(vcat, s.discvars))
78+
push!(alleqs,pdeeqs)
79+
push!(alldepvarsdisc, reduce(vcat, values(s.discvars)))
11380
end
11481
end
11582

@@ -131,6 +98,7 @@ function SciMLBase.symbolic_discretize(pdesys::PDESystem, discretization::Method
13198
return sys, nothing
13299
else
133100
# * In the end we have reduced the problem to a system of equations in terms of Dt that can be solved by the `solve` method.
101+
#println(alleqs, bceqs)
134102
sys = ODESystem(vcat(alleqs, unique(bceqs)), t, vec(reduce(vcat, vec(alldepvarsdisc))), ps, defaults=Dict(defaults), name=pdesys.name)
135103
return sys, tspan
136104
end

src/MOL_utils.jl

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
# Counts the Differential operators for given variable x. This is used to determine
2-
# the order of a PDE.
1+
"""
2+
Counts the Differential operators for given variable x. This is used to determine
3+
the order of a PDE.
4+
"""
35
function count_differentials(term, x::Symbolics.Symbolic)
46
S = Symbolics
57
SU = SymbolicUtils
@@ -15,7 +17,9 @@ function count_differentials(term, x::Symbolics.Symbolic)
1517
end
1618
end
1719

18-
# return list of differential orders in the equation
20+
"""
21+
return list of differential orders in the equation
22+
"""
1923
function differential_order(eq, x::Symbolics.Symbolic)
2024
S = Symbolics
2125
SU = SymbolicUtils
@@ -33,7 +37,9 @@ function differential_order(eq, x::Symbolics.Symbolic)
3337
return filter(!iszero, orders)
3438
end
3539

36-
# find all the dependent variables given by depvar_ops in an expression
40+
"""
41+
find all the dependent variables given by depvar_ops in an expression
42+
"""
3743
function get_depvars(eq,depvar_ops)
3844
S = Symbolics
3945
SU = SymbolicUtils
@@ -51,4 +57,36 @@ function get_depvars(eq,depvar_ops)
5157
end
5258
end
5359
return depvars
54-
end
60+
end
61+
62+
"""
63+
A function that creates a tuple of CartesianIndices of unit length and `N` dimensions, one pointing along each dimension.
64+
"""
65+
function unitindices(N::Int) #create unit CartesianIndex for each dimension
66+
null = zeros(Int, N)
67+
return map(1:N) do i
68+
unit_i = copy(null)
69+
unit_i[i] = 1
70+
CartesianIndex(Tuple(unit_i))
71+
end
72+
end
73+
74+
@inline function unitindex(N, j)
75+
unitindices(N)[j]
76+
end
77+
78+
function split_additive_terms(eq)
79+
# Calling the methods from symbolicutils matches the expressions
80+
rhs_arg = istree(eq.rhs) && (SymbolicUtils.operation(eq.rhs) == +) ? SymbolicUtils.arguments(eq.rhs) : [eq.rhs]
81+
lhs_arg = istree(eq.lhs) && (SymbolicUtils.operation(eq.lhs) == +) ? SymbolicUtils.arguments(eq.lhs) : [eq.lhs]
82+
83+
return vcat(lhs_arg,rhs_arg)
84+
end
85+
@inline clip(II::CartesianIndex{M}, j, N) where M = II[j] > N ? II - unitindices(M)[j] : II
86+
subsmatch(expr, rule) = isequal(substitute(expr, rule), expr) ? false : true
87+
88+
89+
90+
half_range(x) = -div(x,2):div(x,2)
91+
92+

src/MethodOfLines.jl

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,28 @@ module MethodOfLines
22
using LinearAlgebra
33
using SciMLBase
44
using DiffEqBase
5+
using DiffEqOperators
56
using ModelingToolkit
7+
using ModelingToolkit: operation, istree, arguments, variable
68
using SymbolicUtils, Symbolics
9+
using SymbolicUtils: operation, arguments
710
import DomainSets
811

9-
include("MOL_utils.jl")
1012
include("discretization/fornberg.jl")
13+
14+
include("grid_types.jl")
15+
include("MOLFiniteDifference.jl")
16+
include("bcs/boundary_types.jl")
17+
1118
include("discretization/discretize_vars.jl")
12-
include("equation_types.jl")
19+
include("MOL_utils.jl")
20+
21+
include("discretization/differential_discretizer.jl")
1322
include("discretization/generate_finite_difference_rules.jl")
23+
1424
include("bcs/generate_bc_eqs.jl")
1525

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

1828
export MOLFiniteDifference, discretize, symbolic_discretize, grid_align, edge_align, center_align
1929
end

src/bcs/boundary_types.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
### INITIAL AND BOUNDARY CONDITIONS ###
2+
3+
abstract type AbstractBoundary end
4+
5+
abstract type AbstractTruncatingBoundary <: AbstractBoundary end
6+
7+
abstract type AbstractExtendingBoundary <: AbstractBoundary end
8+
9+
struct LowerBoundary <: AbstractTruncatingBoundary
10+
end
11+
12+
struct UpperBoundary<: AbstractTruncatingBoundary
13+
end
14+
15+
struct CompleteBoundary <: AbstractTruncatingBoundary
16+
end
17+
18+
struct PeriodicBoundary <: AbstractBoundary
19+
end
20+
21+
struct BoundaryHandler{hasperiodic}
22+
boundaries::Dict{Num, AbstractBoundary}
23+
end

0 commit comments

Comments
 (0)