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

Commit d5c1c94

Browse files
Merge pull request #511 from xtalax/completedifferences
Complete Differences
2 parents 66d9692 + 473d512 commit d5c1c94

File tree

5 files changed

+198
-23
lines changed

5 files changed

+198
-23
lines changed

src/DiffEqOperators.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ end
7373
export MatrixFreeOperator
7474
export AnalyticalJacVecOperator, JacVecOperator, getops
7575
export AbstractDerivativeOperator, DerivativeOperator,
76-
CenteredDifference, UpwindDifference, nonlinear_diffusion, nonlinear_diffusion!,
76+
CenteredDifference, UpwindDifference, CompleteCenteredDifference, CompleteUpwindDifference, CompleteHalfCenteredDifference, nonlinear_diffusion, nonlinear_diffusion!,
7777
GradientOperator, Gradient, CurlOperator, Curl, DivergenceOperator, Divergence
7878
export DirichletBC, Dirichlet0BC, NeumannBC, Neumann0BC, RobinBC, GeneralBC, MultiDimBC, PeriodicBC,
7979
MultiDimDirectionalBC, ComposedMultiDimBC

src/composite_operators.jl

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,19 @@ struct DiffEqOperatorCombination{T,O<:Tuple{Vararg{AbstractDiffEqLinearOperator{
2929
new{T,typeof(ops),typeof(cache)}(ops, cache)
3030
end
3131
end
32-
+(ops::AbstractDiffEqLinearOperator...) = DiffEqOperatorCombination(ops)
33-
+(op::AbstractDiffEqLinearOperator, A::AbstractMatrix) = op + DiffEqArrayOperator(A)
34-
+(op::AbstractDiffEqLinearOperator{T}, α::UniformScaling) where T = op + UniformScaling(T.λ))(size(op,1))
35-
+(A::AbstractMatrix, op::AbstractDiffEqLinearOperator) = op + A
36-
+::UniformScaling, op::AbstractDiffEqLinearOperator) = op + α
37-
+(L1::DiffEqOperatorCombination, L2::AbstractDiffEqLinearOperator) = DiffEqOperatorCombination((L1.ops..., L2))
38-
+(L1::AbstractDiffEqLinearOperator, L2::DiffEqOperatorCombination) = DiffEqOperatorCombination((L1, L2.ops...))
39-
+(L1::DiffEqOperatorCombination, L2::DiffEqOperatorCombination) = DiffEqOperatorCombination((L1.ops..., L2.ops...))
40-
-(L1::AbstractDiffEqLinearOperator, L2::AbstractDiffEqLinearOperator) = L1 + (-L2)
41-
-(L::AbstractDiffEqLinearOperator, A::AbstractMatrix) = L + (-A)
42-
-(A::AbstractMatrix, L::AbstractDiffEqLinearOperator) = A + (-L)
43-
-(L::AbstractDiffEqLinearOperator, α::UniformScaling) = L + (-α)
44-
-::UniformScaling, L::AbstractDiffEqLinearOperator) = α + (-L)
32+
Base.:+(ops::AbstractDiffEqLinearOperator...) = DiffEqOperatorCombination(ops)
33+
Base.:+(op::AbstractDiffEqLinearOperator, A::AbstractMatrix) = op + DiffEqArrayOperator(A)
34+
Base.:+(op::AbstractDiffEqLinearOperator{T}, α::UniformScaling) where T = op + UniformScaling(T.λ))(size(op,1))
35+
Base.:+(A::AbstractMatrix, op::AbstractDiffEqLinearOperator) = op + A
36+
Base.:+::UniformScaling, op::AbstractDiffEqLinearOperator) = op + α
37+
Base.:+(L1::DiffEqOperatorCombination, L2::AbstractDiffEqLinearOperator) = DiffEqOperatorCombination((L1.ops..., L2))
38+
Base.:+(L1::AbstractDiffEqLinearOperator, L2::DiffEqOperatorCombination) = DiffEqOperatorCombination((L1, L2.ops...))
39+
Base.:+(L1::DiffEqOperatorCombination, L2::DiffEqOperatorCombination) = DiffEqOperatorCombination((L1.ops..., L2.ops...))
40+
Base.:-(L1::AbstractDiffEqLinearOperator, L2::AbstractDiffEqLinearOperator) = L1 + (-L2)
41+
Base.:-(L::AbstractDiffEqLinearOperator, A::AbstractMatrix) = L + (-A)
42+
Base.:-(A::AbstractMatrix, L::AbstractDiffEqLinearOperator) = A + (-L)
43+
Base.:-(L::AbstractDiffEqLinearOperator, α::UniformScaling) = L + (-α)
44+
Base.:-::UniformScaling, L::AbstractDiffEqLinearOperator) = α + (-L)
4545
getops(L::DiffEqOperatorCombination) = L.ops
4646
Matrix(L::DiffEqOperatorCombination) = sum(Matrix, L.ops)
4747
convert(::Type{AbstractMatrix}, L::DiffEqOperatorCombination) =
@@ -51,8 +51,8 @@ SparseArrays.sparse(L::DiffEqOperatorCombination) = sum(sparse1, L.ops)
5151
size(L::DiffEqOperatorCombination, args...) = size(L.ops[1], args...)
5252
getindex(L::DiffEqOperatorCombination, i::Int) = sum(op -> op[i], L.ops)
5353
getindex(L::DiffEqOperatorCombination, I::Vararg{Int, N}) where {N} = sum(op -> op[I...], L.ops)
54-
*(L::DiffEqOperatorCombination, x::AbstractArray) = sum(op -> op * x, L.ops)
55-
*(x::AbstractArray, L::DiffEqOperatorCombination) = sum(op -> x * op, L.ops)
54+
Base.:*(L::DiffEqOperatorCombination, x::AbstractArray) = sum(op -> op * x, L.ops)
55+
Base.:*(x::AbstractArray, L::DiffEqOperatorCombination) = sum(op -> x * op, L.ops)
5656
/(L::DiffEqOperatorCombination, x::AbstractArray) = sum(op -> op / x, L.ops)
5757
\(x::AbstractArray, L::DiffEqOperatorCombination) = sum(op -> x \ op, L.ops)
5858
function mul!(y::AbstractVector, L::DiffEqOperatorCombination, b::AbstractVector)
@@ -88,13 +88,20 @@ struct DiffEqOperatorComposition{T,O<:Tuple{Vararg{AbstractDiffEqLinearOperator{
8888
new{T,typeof(ops),typeof(caches)}(ops, caches)
8989
end
9090
end
91-
*(ops::AbstractDiffEqLinearOperator...) = DiffEqOperatorComposition(reverse(ops))
91+
# this is needed to not break dispatch in MethodOfLines
92+
function Base.:*(ops::AbstractDiffEqLinearOperator...)
93+
try
94+
return DiffEqOperatorComposition(reverse(ops))
95+
catch e
96+
return 1
97+
end
98+
end
9299
(L1::AbstractDiffEqLinearOperator, L2::AbstractDiffEqLinearOperator) = DiffEqOperatorComposition((L2, L1))
93-
*(L1::DiffEqOperatorComposition, L2::AbstractDiffEqLinearOperator) = DiffEqOperatorComposition((L2, L1.ops...))
100+
Base.:*(L1::DiffEqOperatorComposition, L2::AbstractDiffEqLinearOperator) = DiffEqOperatorComposition((L2, L1.ops...))
94101
(L1::DiffEqOperatorComposition, L2::AbstractDiffEqLinearOperator) = DiffEqOperatorComposition((L2, L1.ops...))
95-
*(L1::AbstractDiffEqLinearOperator, L2::DiffEqOperatorComposition) = DiffEqOperatorComposition((L2.ops..., L1))
102+
Base.:*(L1::AbstractDiffEqLinearOperator, L2::DiffEqOperatorComposition) = DiffEqOperatorComposition((L2.ops..., L1))
96103
(L1::AbstractDiffEqLinearOperator, L2::DiffEqOperatorComposition) = DiffEqOperatorComposition((L2.ops..., L1))
97-
*(L1::DiffEqOperatorComposition, L2::DiffEqOperatorComposition) = DiffEqOperatorComposition((L2.ops..., L1.ops...))
104+
Base.:*(L1::DiffEqOperatorComposition, L2::DiffEqOperatorComposition) = DiffEqOperatorComposition((L2.ops..., L1.ops...))
98105
(L1::DiffEqOperatorComposition, L2::DiffEqOperatorComposition) = DiffEqOperatorComposition((L2.ops..., L1.ops...))
99106
getops(L::DiffEqOperatorComposition) = L.ops
100107
Matrix(L::DiffEqOperatorComposition) = prod(Matrix, reverse(L.ops))
@@ -105,8 +112,8 @@ SparseArrays.sparse(L::DiffEqOperatorComposition) = prod(sparse1, reverse(L.ops)
105112
size(L::DiffEqOperatorComposition) = (size(L.ops[end], 1), size(L.ops[1], 2))
106113
size(L::DiffEqOperatorComposition, m::Integer) = size(L)[m]
107114
opnorm(L::DiffEqOperatorComposition) = prod(opnorm, L.ops)
108-
*(L::DiffEqOperatorComposition, x::AbstractArray) = foldl((acc, op) -> op*acc, L.ops; init=x)
109-
*(x::AbstractArray, L::DiffEqOperatorComposition) = foldl((acc, op) -> acc*op, reverse(L.ops); init=x)
115+
Base.:*(L::DiffEqOperatorComposition, x::AbstractArray) = foldl((acc, op) -> op*acc, L.ops; init=x)
116+
Base.:*(x::AbstractArray, L::DiffEqOperatorComposition) = foldl((acc, op) -> acc*op, reverse(L.ops); init=x)
110117
/(L::DiffEqOperatorComposition, x::AbstractArray) = foldl((acc, op) -> op/acc, L.ops; init=x)
111118
/(x::AbstractArray, L::DiffEqOperatorComposition) = foldl((acc, op) -> acc/op, L.ops; init=x)
112119
\(L::DiffEqOperatorComposition, x::AbstractArray) = foldl((acc, op) -> op\acc, reverse(L.ops); init=x)

src/derivative_operators/derivative_operator.jl

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ function CenteredDifference{N}(derivative_order::Int,
105105
)
106106
end
107107

108+
109+
108110
function generate_coordinates(i::Int, stencil_x, dummy_x, dx::AbstractVector{T}) where T <: Real
109111
len = length(stencil_x)
110112
stencil_x .= stencil_x.*zero(T)
@@ -161,6 +163,98 @@ function CenteredDifference{N}(derivative_order::Int,
161163
)
162164
end
163165

166+
167+
# TODO: Set weight to zero if its below a certain threshold
168+
"""
169+
A helper function to compute the coefficients of a derivative operator including the boundary coefficients in the centered scheme.
170+
"""
171+
function CompleteCenteredDifference(derivative_order::Int,
172+
approximation_order::Int, dx::T) where {T<:Real,N}
173+
@assert approximation_order>1 "approximation_order must be greater than 1."
174+
stencil_length = derivative_order + approximation_order - 1 + (derivative_order+approximation_order)%2
175+
boundary_stencil_length = derivative_order + approximation_order
176+
dummy_x = -div(stencil_length,2) : div(stencil_length,2)
177+
left_boundary_x = 0:(boundary_stencil_length-1)
178+
right_boundary_x = reverse(-boundary_stencil_length+1:0)
179+
180+
boundary_point_count = div(stencil_length,2) # -1 due to the ghost point
181+
# Because it's a N x (N+2) operator, the last stencil on the sides are the [b,0,x,x,x,x] stencils, not the [0,x,x,x,x,x] stencils, since we're never solving for the derivative at the boundary point.
182+
#deriv_spots = (-div(stencil_length,2)+1) : -1 # unused
183+
L_boundary_deriv_spots = left_boundary_x[1:div(stencil_length,2)]
184+
185+
stencil_coefs = convert(SVector{stencil_length, T}, (1/dx^derivative_order) * calculate_weights(derivative_order, zero(T), dummy_x))
186+
_low_boundary_coefs = SVector{boundary_stencil_length, T}[convert(SVector{boundary_stencil_length, T}, (1/dx^derivative_order) * calculate_weights(derivative_order, oneunit(T)*x0, left_boundary_x)) for x0 in L_boundary_deriv_spots]
187+
low_boundary_coefs = convert(SVector{boundary_point_count},vcat(_low_boundary_coefs))
188+
189+
# _high_boundary_coefs = SVector{boundary_stencil_length, T}[convert(SVector{boundary_stencil_length, T}, (1/dx^derivative_order) * calculate_weights(derivative_order, oneunit(T)*x0, reverse(right_boundary_x))) for x0 in R_boundary_deriv_spots]
190+
high_boundary_coefs = convert(SVector{boundary_point_count},[reverse(v)*(-1)^derivative_order for v in _low_boundary_coefs])
191+
192+
offside = 0
193+
194+
coefficients = nothing
195+
196+
197+
DerivativeOperator{T,Nothing,false,T,typeof(stencil_coefs),
198+
typeof(low_boundary_coefs),typeof(high_boundary_coefs),typeof(coefficients),
199+
Nothing}(
200+
derivative_order, approximation_order, dx, 1, stencil_length,
201+
stencil_coefs,
202+
boundary_stencil_length,
203+
boundary_point_count,
204+
low_boundary_coefs,
205+
high_boundary_coefs,offside,coefficients,nothing
206+
)
207+
end
208+
209+
210+
"""
211+
A helper function to compute the coefficients of a derivative operator including the boundary coefficients in the half offset centered scheme. See table 2 in https://web.njit.edu/~jiang/math712/fornberg.pdf
212+
"""
213+
function CompleteHalfCenteredDifference(derivative_order::Int,
214+
approximation_order::Int, dx::T) where {T<:Real,N}
215+
@assert approximation_order>1 "approximation_order must be greater than 1."
216+
centered_stencil_length = approximation_order + 2*Int(floor(derivative_order/2)) + (approximation_order%2)
217+
boundary_stencil_length = derivative_order + approximation_order
218+
endpoint = div(centered_stencil_length, 2)
219+
dummy_x = 1-endpoint : endpoint
220+
left_boundary_x = 1:(boundary_stencil_length)
221+
right_boundary_x = reverse(-boundary_stencil_length:-1)
222+
223+
boundary_point_count = div(centered_stencil_length,2) # -1 due to the ghost point
224+
# ? Is fornberg valid when taking an x0 outside of the stencil i.e at the boundary?
225+
xoffset = range(1.5, length=boundary_point_count, step = 1.0)
226+
227+
# Because it's a N x (N+2) operator, the last stencil on the sides are the [b,0,x,x,x,x] stencils, not the [0,x,x,x,x,x] stencils, since we're never solving for the derivative at the boundary point.
228+
#deriv_spots = (-div(stencil_length,2)+1) : -1 # unused
229+
L_boundary_deriv_spots = xoffset[1:div(centered_stencil_length,2)]
230+
231+
stencil_coefs = convert(SVector{centered_stencil_length, T}, (1/dx^derivative_order) * calculate_weights(derivative_order, convert(T, 0.5), dummy_x))
232+
# For each boundary point, for each tappoint in the half offset central difference stencil, we need to calculate the coefficients for the stencil.
233+
234+
235+
_low_boundary_coefs = [convert(SVector{boundary_stencil_length, T}, (1/dx^derivative_order) * calculate_weights(derivative_order, offset, left_boundary_x)) for offset in L_boundary_deriv_spots]
236+
low_boundary_coefs = convert(SVector{boundary_point_count}, _low_boundary_coefs)
237+
238+
_high_boundary_coefs = [reverse(stencil)*(-1)^derivative_order for stencil in low_boundary_coefs]
239+
high_boundary_coefs = convert(SVector{boundary_point_count}, _high_boundary_coefs)
240+
# _high_boundary_coefs = SVector{boundary_stencil_length, T}[convert(SVector{boundary_stencil_length, T}, (1/dx^derivative_order) * calculate_weights(derivative_order, oneunit(T)*x0, reverse(right_boundary_x))) for x0 in R_boundary_deriv_spots]
241+
242+
offside = 0
243+
244+
coefficients = nothing
245+
246+
247+
DerivativeOperator{T,Nothing,false,T,typeof(stencil_coefs),
248+
typeof(low_boundary_coefs),typeof(high_boundary_coefs),typeof(coefficients),Nothing}(
249+
derivative_order, approximation_order, dx, 1, centered_stencil_length,
250+
stencil_coefs,
251+
boundary_stencil_length,
252+
boundary_point_count,
253+
low_boundary_coefs,
254+
high_boundary_coefs,offside,coefficients,nothing
255+
)
256+
end
257+
164258
struct UpwindDifference{N} end
165259

166260
"""
@@ -287,6 +381,50 @@ function UpwindDifference{N}(derivative_order::Int,
287381
)
288382
end
289383

384+
"""
385+
A helper function to compute the coefficients of a derivative operator including the boundary coefficients in the upwind scheme.
386+
"""
387+
function CompleteUpwindDifference(derivative_order::Int,
388+
approximation_order::Int, dx::T,
389+
offside::Int=0) where {T<:Real,N}
390+
391+
@assert offside > -1 "Number of offside points should be non-negative"
392+
@assert offside <= div(derivative_order + approximation_order - 1,2) "Number of offside points should not exceed the primary wind points"
393+
394+
stencil_length = derivative_order + approximation_order
395+
boundary_stencil_length = derivative_order + approximation_order
396+
boundary_point_count = boundary_stencil_length - 1 - offside
397+
398+
# TODO: Clean up the implementation here so that it is more readable and easier to extend in the future
399+
dummy_x = (0.0 - offside) : stencil_length - 1.0 - offside
400+
stencil_coefs = convert(SVector{stencil_length, T}, (1/dx^derivative_order) * calculate_weights(derivative_order, 0.0, dummy_x))
401+
402+
low_boundary_x = 0.0:(boundary_stencil_length-1)
403+
L_boundary_deriv_spots = 0.0:boundary_stencil_length - 2.0 - offside
404+
_low_boundary_coefs = SVector{boundary_stencil_length, T}[convert(SVector{boundary_stencil_length, T}, (1/dx^derivative_order) * calculate_weights(derivative_order, oneunit(T)*x0, low_boundary_x)) for x0 in L_boundary_deriv_spots]
405+
low_boundary_coefs = convert(SVector{boundary_point_count},_low_boundary_coefs)
406+
407+
high_boundary_x = 0.0:-1.0:-(boundary_stencil_length-1.0)
408+
R_boundary_deriv_spots = 0.0:-1.0:-(boundary_stencil_length-2.0)
409+
_high_boundary_coefs = SVector{boundary_stencil_length, T}[convert(SVector{boundary_stencil_length, T}, ((-1/dx)^derivative_order) * calculate_weights(derivative_order, oneunit(T)*x0, high_boundary_x)) for x0 in R_boundary_deriv_spots]
410+
high_boundary_coefs = convert(SVector{boundary_point_count + offside},_high_boundary_coefs)
411+
412+
coefficients = fill!(Vector{T}(undef,len),0)
413+
414+
415+
DerivativeOperator{T,N,true,T,typeof(stencil_coefs),
416+
typeof(low_boundary_coefs),typeof(high_boundary_coefs),Vector{T},
417+
typeof(coeff_func)}(
418+
derivative_order, approximation_order, dx, len, stencil_length,
419+
stencil_coefs,
420+
boundary_stencil_length,
421+
boundary_point_count,
422+
low_boundary_coefs,
423+
high_boundary_coefs,offside,coefficients,coeff_func
424+
)
425+
end
426+
427+
290428
CenteredDifference(args...) = CenteredDifference{1}(args...)
291429
UpwindDifference(args...;kwargs...) = UpwindDifference{1}(args...;kwargs...)
292430
nonlinear_diffusion(args...) = nonlinear_diffusion{1}(args...)

src/derivative_operators/fornberg.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ function calculate_weights(order::Int, x0::T, x::AbstractVector) where T<:Real
5555
http://epubs.siam.org/doi/pdf/10.1137/S0036144596322507 - Modified Fornberg Algorithm
5656
=#
5757
_C = C[:,end]
58-
_C[div(N,2)+1] -= sum(_C)
58+
if order != 0
59+
_C[div(N,2)+1] -= sum(_C)
60+
end
5961
return _C
6062
end

test/DerivativeOperators/derivative_operators_interface.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,34 @@ end
144144
@test BandedMatrix(L) correct
145145
end
146146

147+
@testset "Correctness of Uniform Stencils, Complete" begin
148+
weights = []
149+
150+
push!(weights, ([-0.5,0,0.5], [1.,-2.,1.], [-1/2,1.,0.,-1.,1/2]))
151+
push!(weights, ([1/12, -2/3,0,2/3,-1/12], [-1/12,4/3,-5/2,4/3,-1/12], [1/8,-1.,13/8,0.,-13/8,1.,-1/8]))
152+
153+
for d in 1:3
154+
for (i,a) in enumerate([2,4])
155+
D = CompleteCenteredDifference(d,a,1.0)
156+
157+
@test all(isapprox.(D.stencil_coefs, weights[i][d], atol=1e-10))
158+
end
159+
end
160+
end
161+
162+
@testset "Correctness of Uniform Stencils, Complete Half" begin
163+
weights = (([.5, .5], [-1/16, 9/16, 9/16, -1/16]),
164+
([-1., 1.], [1/24, -9/8, 9/8, -1/24]))
165+
for (i,a) in enumerate([2,4])
166+
for d in 0:1
167+
D = CompleteHalfCenteredDifference(d,a,1.0)
168+
@test all(D.stencil_coefs .≈ weights[d+1][i])
169+
end
170+
end
171+
172+
end
173+
174+
147175
@testset "Correctness of Non-Uniform Stencils" begin
148176
x = [0., 0.08, 0.1, 0.15, 0.19, 0.26, 0.29]
149177
nx = length(x)

0 commit comments

Comments
 (0)