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

Commit 31e316e

Browse files
Merge remote-tracking branch 'upstream/master'
2 parents 6bff8b9 + e5de70f commit 31e316e

49 files changed

Lines changed: 1318 additions & 1343 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/CI.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ jobs:
1212
strategy:
1313
matrix:
1414
group:
15-
- Interface
15+
- OperatorInterface
16+
- MOLFiniteDifference
17+
- Multithreading
18+
- Misc
1619
steps:
1720
- uses: actions/checkout@v2
1821
- uses: julia-actions/setup-julia@v1

docs/make.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ makedocs(
1515
"Symbolic Tutorials" => [
1616
"symbolic_tutorials/mol_heat.md"
1717
],
18+
"Operator Tutorials" => [
19+
"operator_tutorials/kdv.md"
20+
],
1821
"Operators" => [
1922
"operators/operator_overview.md",
2023
"operators/derivative_operators.md",
@@ -26,8 +29,8 @@ makedocs(
2629
],
2730
"Nonlinear Derivatives" => [
2831
"nonlinear_derivatives/nonlinear_diffusion.md"
29-
]
30-
]
32+
]
33+
]
3134
)
3235

3336
deploydocs(

docs/src/operator_tutorials/kdv.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Solving KdV Solitons with Upwinding Operators
2+
3+
The KdV equation is of the form `uₜ + αuuₓ + βuₓₓₓ = 0`. Here we'll use `α = 6`, `β = 1` for
4+
simplicity of the true solution expression.
5+
6+
### 1-Soliton solution using Upwind Difference
7+
8+
The analytical expression for the single soliton case takes the form `u(x,t) = (c/2)/cosh²(√c * ξ/2)`.
9+
10+
`c > 0` (wave speed) ; `ξ = x - c*t` (moving coordinate)
11+
12+
```julia
13+
using Test
14+
using DiffEqOperators, OrdinaryDiffEq, LinearAlgebra
15+
16+
# Space domain and grids
17+
N = 21
18+
Δx = 1/(N-1)
19+
c = 1
20+
x = -10:Δx:10;
21+
22+
# solution of the single forward moving wave
23+
ϕ(x,t) = (1/2)*sech.((x .- t)/2).^2
24+
25+
# Discretizing the PDE at t = 0
26+
u0 = ϕ(x,0);
27+
du = zeros(size(x));
28+
29+
# Declaring the Upwind operator with winding = -1 since the wave travels from left to right
30+
A = UpwindDifference{Float64}(1,3,Δx,length(x),-1);
31+
32+
# Defining the ODE problem
33+
function KdV(du, u, p, t)
34+
bc = GeneralBC([0,1,-6*ϕ(-10,t),0,-1],[0,1,-6*ϕ(10,t),0,-1],Δx,3)
35+
mul!(du,A,bc*u)
36+
end
37+
38+
single_solition = ODEProblem(KdV, u0, (0.,5.));
39+
40+
# Solving the ODE problem
41+
soln = solve(single_solition,Tsit5(),abstol=1e-6,reltol=1e-6);
42+
43+
# Plotting the results, comparing Analytical and Numerical solutions
44+
using Plots
45+
plot(ϕ(x,0), title = "Single forward moving wave", yaxis="u(x,t)", label = "t = 0.0 (Analytic)")
46+
plot!(ϕ(x,2), label = "t = 2.0 (Analytic)")
47+
plot!(soln(0.0), label = "t = 0.0 (Numerical)",ls = :dash)
48+
plot!(soln(2.0), label = "t = 2.0 (Numerical)",ls = :dash)
49+
```
50+
![solution_plot](https://user-images.githubusercontent.com/39168576/111033316-a702a180-8436-11eb-9b8e-749ad9206639.jpeg)

docs/src/symbolic_tutorials/mol_heat.md

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ bcs = [u(0,x) ~ cos(x),
2525

2626
# Space and time domains
2727
domains = [t IntervalDomain(0.0,1.0),
28-
x IntervalDomain(0.0,1.0)]
28+
x IntervalDomain(0.0,1.0)]
2929

3030
# PDE system
31-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u])
31+
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
3232

3333
# Method of lines discretization
3434
dx = 0.1
3535
order = 2
36-
discretization = MOLFiniteDifference(dx,order)
36+
discretization = MOLFiniteDifference([x=>dx],t)
3737

3838
# Convert the PDE problem into an ODE problem
3939
prob = discretize(pdesys,discretization)
@@ -43,16 +43,18 @@ using OrdinaryDiffEq
4343
sol = solve(prob,Tsit5(),saveat=0.2)
4444

4545
# Plot results and compare with exact solution
46-
x = prob.space[2]
46+
x = (0:dx:1)[2:end-1]
4747
t = sol.t
4848

4949
using Plots
5050
plt = plot()
51+
5152
for i in 1:length(t)
52-
plot!(x,Array(prob.extrapolation[1](t[i])*sol.u[i]),label="Numerical, t=$(t[i])")
53+
plot!(x,sol.u[i],label="Numerical, t=$(t[i])")
5354
scatter!(x, u_exact(x, t[i]),label="Exact, t=$(t[i])")
5455
end
5556
display(plt)
57+
savefig("plot.png")
5658
```
5759
### Neumann boundary conditions
5860

@@ -79,13 +81,13 @@ domains = [t ∈ IntervalDomain(0.0,1.0),
7981
x IntervalDomain(0.0,1.0)]
8082

8183
# PDE system
82-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u])
84+
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
8385

8486
# Method of lines discretization
8587
# Need a small dx here for accuracy
8688
dx = 0.01
8789
order = 2
88-
discretization = MOLFiniteDifference(dx,order)
90+
discretization = MOLFiniteDifference([x=>dx],t)
8991

9092
# Convert the PDE problem into an ODE problem
9193
prob = discretize(pdesys,discretization)
@@ -95,16 +97,18 @@ using OrdinaryDiffEq
9597
sol = solve(prob,Tsit5(),saveat=0.2)
9698

9799
# Plot results and compare with exact solution
98-
x = prob.space[2]
100+
x = (0:dx:1)[2:end-1]
99101
t = sol.t
100102

101103
using Plots
102104
plt = plot()
105+
103106
for i in 1:length(t)
104-
plot!(x,Array(prob.extrapolation[1](t[i])*sol.u[i]),label="Numerical, t=$(t[i])")
107+
plot!(x,sol.u[i],label="Numerical, t=$(t[i])",lw=12)
105108
scatter!(x, u_exact(x, t[i]),label="Exact, t=$(t[i])")
106109
end
107110
display(plt)
111+
savefig("plot.png")
108112
```
109113

110114
### Robin boundary conditions
@@ -132,13 +136,13 @@ domains = [t ∈ IntervalDomain(0.0,1.0),
132136
x IntervalDomain(-1.0,1.0)]
133137

134138
# PDE system
135-
pdesys = PDESystem(eq,bcs,domains,[t,x],[u])
139+
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
136140

137141
# Method of lines discretization
138142
# Need a small dx here for accuracy
139143
dx = 0.05
140144
order = 2
141-
discretization = MOLFiniteDifference(dx,order)
145+
discretization = MOLFiniteDifference([x=>dx],t)
142146

143147
# Convert the PDE problem into an ODE problem
144148
prob = discretize(pdesys,discretization)
@@ -148,14 +152,16 @@ using OrdinaryDiffEq
148152
sol = solve(prob,Tsit5(),saveat=0.2)
149153

150154
# Plot results and compare with exact solution
151-
x = prob.space[2]
155+
x = (0:dx:1)[2:end-1]
152156
t = sol.t
153157

154158
using Plots
155159
plt = plot()
160+
156161
for i in 1:length(t)
157-
plot!(x,Array(prob.extrapolation[1](t[i])*sol.u[i]),label="Numerical, t=$(t[i])")
162+
plot!(x,sol.u[i],label="Numerical, t=$(t[i])")
158163
scatter!(x, u_exact(x, t[i]),label="Exact, t=$(t[i])")
159164
end
160165
display(plt)
166+
savefig("plot.png")
161167
```

src/DiffEqOperators.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ include("derivative_operators/coefficient_functions.jl")
4242

4343
### Composite Operators
4444
include("composite_operators.jl")
45-
46-
include("MOL_discretization.jl")
47-
4845
include("docstrings.jl")
4946

5047
### Concretizations
5148
include("derivative_operators/concretization.jl")
5249

50+
### MOL
51+
include("MOLFiniteDifference/MOL_discretization.jl")
52+
5353
# The (u,p,t) and (du,u,p,t) interface
5454
for T in [DiffEqScaledOperator, DiffEqOperatorCombination, DiffEqOperatorComposition, GhostDerivativeOperator]
5555
(L::T)(u,p,t) = (update_coefficients!(L,u,p,t); L * u)
@@ -63,6 +63,7 @@ export AbstractDerivativeOperator, DerivativeOperator,
6363
export DirichletBC, Dirichlet0BC, NeumannBC, Neumann0BC, RobinBC, GeneralBC, MultiDimBC, PeriodicBC,
6464
MultiDimDirectionalBC, ComposedMultiDimBC
6565
export compose, decompose, perpsize
66+
export discretize, symbolic_discretize
6667

6768
export GhostDerivativeOperator
6869
export MOLFiniteDifference
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
using ModelingToolkit: operation, istree, arguments
2+
# Method of lines discretization scheme
3+
struct MOLFiniteDifference{T,T2} <: DiffEqBase.AbstractDiscretization
4+
dxs::T
5+
time::T2
6+
upwind_order::Int
7+
centered_order::Int
8+
end
9+
10+
# Constructors. If no order is specified, both upwind and centered differences will be 2nd order
11+
MOLFiniteDifference(dxs, time; upwind_order = 1, centered_order = 2) =
12+
MOLFiniteDifference(dxs, time, upwind_order, centered_order)
13+
14+
function SciMLBase.symbolic_discretize(pdesys::ModelingToolkit.PDESystem,discretization::DiffEqOperators.MOLFiniteDifference)
15+
pdeeqs = pdesys.eqs isa Vector ? pdesys.eqs : [pdesys.eqs]
16+
t = discretization.time
17+
nottime = filter(x->x.val != t.val,pdesys.indvars)
18+
19+
# Discretize space
20+
21+
space = map(nottime) do x
22+
xdomain = pdesys.domain[findfirst(d->x.val == d.variables,pdesys.domain)]
23+
@assert xdomain.domain isa IntervalDomain
24+
dx = discretization.dxs[findfirst(dxs->x.val == dxs[1].val,discretization.dxs)][2]
25+
dx isa Number ? (xdomain.domain.lower:dx:xdomain.domain.upper) : dx
26+
end
27+
tdomain = pdesys.domain[findfirst(d->t.val == d.variables,pdesys.domain)]
28+
@assert tdomain.domain isa IntervalDomain
29+
tspan = (tdomain.domain.lower,tdomain.domain.upper)
30+
31+
# Build symbolic variables
32+
indices = CartesianIndices(((axes(s)[1] for s in space)...,))
33+
depvars = map(pdesys.depvars) do u
34+
[Num(Variable{Symbolics.FnType{Tuple{Any}, Real}}(Base.nameof(ModelingToolkit.operation(u.val)),II.I...))(t) for II in indices]
35+
end
36+
spacevals = map(y->[Pair(nottime[i],space[i][y.I[i]]) for i in 1:length(nottime)],indices)
37+
38+
# Build symbolic maps
39+
edges = reduce(vcat,[[vcat([Colon() for j in 1:i-1],1,[Colon() for j in i+1:length(nottime)]),
40+
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)])
41+
42+
#edgeindices = [indices[e...] for e in edges]
43+
edgevals = reduce(vcat,[[nottime[i]=>first(space[i]),nottime[i]=>last(space[i])] for i in 1:length(space)])
44+
edgevars = [[d[e...] for e in edges] for d in depvars]
45+
edgemaps = [spacevals[e...] for e in edges]
46+
initmaps = substitute.(pdesys.depvars,[t=>tspan[1]])
47+
48+
depvarmaps = reduce(vcat,[substitute.((pdesys.depvars[i],),edgevals) .=> edgevars[i] for i in 1:length(pdesys.depvars)])
49+
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]])
52+
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)])
58+
else
59+
# TODO: Fix Neumann and Robin on higher dimension
60+
depvarderivmaps = []
61+
end
62+
63+
# Generate initial conditions and bc equations
64+
u0 = []
65+
bceqs = []
66+
for bc in pdesys.bcs
67+
if ModelingToolkit.operation(bc.lhs) isa Sym && t.val ModelingToolkit.arguments(bc.lhs)
68+
# initial condition
69+
# Assume in the form `u(...) ~ ...` for now
70+
push!(u0,vec(depvars[findfirst(isequal(bc.lhs),initmaps)] .=> substitute.((bc.rhs,),spacevals)))
71+
else
72+
# Algebraic equations for BCs
73+
i = findfirst(x->occursin(x.val,bc.lhs),first.(depvarmaps))
74+
75+
# TODO: Fix Neumann and Robin on higher dimension
76+
lhs = length(nottime) == 1 ? substitute(bc.lhs,depvarderivmaps[i]) : bc.lhs
77+
78+
lhs = substitute(lhs,depvarmaps[i])
79+
rhs = substitute.((bc.rhs,),edgemaps[i])
80+
lhs = lhs isa Vector ? lhs : [lhs] # handle 1D
81+
push!(bceqs,lhs .~ rhs)
82+
end
83+
end
84+
u0 = reduce(vcat,u0)
85+
bceqs = reduce(vcat,bceqs)
86+
87+
# Generate PDE Equations
88+
interior = indices[[2:length(s)-1 for s in space]...]
89+
eqs = vec(map(Base.product(interior,pdeeqs)) do p
90+
i,eq = p
91+
92+
# TODO: Number of points in the central_neighbor_idxs should be dependent
93+
# on discretization.centered_order
94+
# TODO: Generalize central difference handling to allow higher even order derivatives
95+
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)]
98+
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)])
100+
101+
# TODO: Use rule matching for nonlinear Laplacian
102+
103+
# TODO: upwind rules needs interpolation into `@rule`
104+
#forward_weights(i,j) = DiffEqOperators.calculate_weights(discretization.upwind_order, 0.0, [space[j][i[j]],space[j][i[j]+1]])
105+
#reverse_weights(i,j) = DiffEqOperators.calculate_weights(discretization.upwind_order, 0.0, [space[j][i[j]-1],space[j][i[j]]])
106+
#upwinding_rules = [@rule(*(~~a,(Differential(nottime[j]))(u),~~b) => IfElse.ifelse(*(~~a..., ~~b...,)>0,
107+
# *(~~a..., ~~b..., dot(reverse_weights(i,j),depvars[k][central_neighbor_idxs(i,j)[1:2]])),
108+
# *(~~a..., ~~b..., dot(forward_weights(i,j),depvars[k][central_neighbor_idxs(i,j)[2:3]]))))
109+
# for j in 1:length(nottime), k in 1:length(pdesys.depvars)]
110+
111+
substitute(eq.lhs,vcat(vec(central_deriv_rules),valrules)) ~ substitute(eq.rhs,vcat(vec(central_deriv_rules),valrules))
112+
end)
113+
114+
# Finalize
115+
defaults = pdesys.ps === nothing || pdesys.ps === SciMLBase.NullParameters() ? u0 : vcat(u0,pdesys.ps)
116+
ps = pdesys.ps === nothing || pdesys.ps === SciMLBase.NullParameters() ? Num[] : first.(pdesys.ps)
117+
sys = ODESystem(vcat(eqs,unique(bceqs)),t,vec(reduce(vcat,vec(depvars))),ps,defaults=Dict(defaults))
118+
sys, tspan
119+
end
120+
121+
function SciMLBase.discretize(pdesys::ModelingToolkit.PDESystem,discretization::DiffEqOperators.MOLFiniteDifference)
122+
sys, tspan = SciMLBase.symbolic_discretize(pdesys,discretization)
123+
simpsys = structural_simplify(sys)
124+
prob = ODEProblem(simpsys,Pair[],tspan)
125+
end
126+
127+
# Piracy, to be deleted when https://github.com/JuliaSymbolics/SymbolicUtils.jl/pull/251
128+
# merges
129+
Base.occursin(needle::ModelingToolkit.SymbolicUtils.Symbolic, haystack::ModelingToolkit.SymbolicUtils.Symbolic) = _occursin(needle, haystack)
130+
Base.occursin(needle, haystack::ModelingToolkit.SymbolicUtils.Symbolic) = _occursin(needle, haystack)
131+
Base.occursin(needle::ModelingToolkit.SymbolicUtils.Symbolic, haystack) = _occursin(needle, haystack)
132+
function _occursin(needle, haystack)
133+
isequal(needle, haystack) && return true
134+
135+
if istree(haystack)
136+
args = arguments(haystack)
137+
for arg in args
138+
occursin(needle, arg) && return true
139+
end
140+
end
141+
return false
142+
end

0 commit comments

Comments
 (0)