Skip to content

Commit ad47cfb

Browse files
SKopeczranocha
andauthored
Careful handling of data types (Float32 & units) (#108)
* show u and sigma in MPRK43 during perform_step * more output * more output * more output * changed MPRK43II small_constant * removed addtional output * format * updated doc strings * added conversion to Float32 in SSPMPRK43 * Added small_constant_function_MPRK43II * revised docstrings * format * typo * Evaluation of PDSFunction and ConservativePDSFunction can handle units * structure runtest * bugfix runtest * additoinal tests * Use Unitful in tests * More functionality from StaticArrays * bugfixes * Reset evaluation of PDSFunctions with sparse prototyp * bugfix * Evaluation of PDSFunctions with sparse Prototype and units is working * format * Enable solution of PDS with unity by OrdinaryDiffEq solvers * test more OrdinaryDiffEq algorithms solving problems with units * Float32 test * removed tests for solving PDSProblems with units by implicit solvers * bugfix test Compatibility of OrdinarDiffEq solvers with (Conservative)PDSProblems and units * Don't use implicit OrdinaryDiffEq solvers with units in tests * Update src/mprk.jl Co-authored-by: Hendrik Ranocha <[email protected]> * Update src/mprk.jl Co-authored-by: Hendrik Ranocha <[email protected]> * Update src/proddest.jl Co-authored-by: Hendrik Ranocha <[email protected]> * Update src/sspmprk.jl Co-authored-by: Hendrik Ranocha <[email protected]> * Update test/runtests.jl Co-authored-by: Hendrik Ranocha <[email protected]> * use unitful.jl's ustirp in tests * Compare unitful data without ustrip * Use ustrip before comparing solutions with units in tests * Added missing test (SSPMPRK43 and Float32) * similar and oneunit * similar(P[:,1]) * set version to v0.2.6 --------- Co-authored-by: Hendrik Ranocha <[email protected]>
1 parent e9de366 commit ad47cfb

File tree

6 files changed

+369
-49
lines changed

6 files changed

+369
-49
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "PositiveIntegrators"
22
uuid = "d1b20bf0-b083-4985-a874-dc5121669aa5"
33
authors = ["Stefan Kopecz, Hendrik Ranocha, and contributors"]
4-
version = "0.2.5"
4+
version = "0.2.6"
55

66
[deps]
77
FastBroadcast = "7034ab61-46d4-4ed7-9d0f-46aef9175898"

src/mprk.jl

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -987,8 +987,9 @@ You can optionally choose the linear solver to be used by passing an
987987
algorithm from [LinearSolve.jl](https://github.com/SciML/LinearSolve.jl)
988988
as keyword argument `linsolve`.
989989
You can also choose the parameter `small_constant` which is added to all Patankar-weight denominators
990-
to avoid divisions by zero. You can pass a value explicitly, otherwise `small_constant` is set to
991-
`floatmin` of the floating point type used.
990+
to avoid divisions by zero. To display the default value for data type `type` evaluate
991+
`MPRK43II(gamma).small_constant_function(type)`, where `type` can be, e.g.,
992+
`Float64`.
992993
993994
## References
994995
@@ -1004,10 +1005,18 @@ struct MPRK43II{T, F, T2} <: OrdinaryDiffEqAdaptiveAlgorithm
10041005
small_constant_function::T2
10051006
end
10061007

1007-
function MPRK43II(gamma; linsolve = LUFactorization(), small_constant = nothing)
1008-
if isnothing(small_constant)
1009-
small_constant_function = floatmin
1010-
elseif small_constant isa Number
1008+
function small_constant_function_MPRK43II(type)
1009+
if type == Float64
1010+
small_constant = 1e-50
1011+
else
1012+
small_constant = floatmin(type)
1013+
end
1014+
return small_constant
1015+
end
1016+
1017+
function MPRK43II(gamma; linsolve = LUFactorization(),
1018+
small_constant = small_constant_function_MPRK43II)
1019+
if small_constant isa Number
10111020
small_constant_function = Returns(small_constant)
10121021
else # assume small_constant isa Function
10131022
small_constant_function = small_constant

src/proddest.jl

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,11 @@ function PDSProblem{iip}(P, D, u0, tspan, p = NullParameters();
9595

9696
# p_prototype is used to store evaluations of P, if P is in-place.
9797
if isnothing(p_prototype) && iip
98-
p_prototype = zeros(eltype(u0), (length(u0), length(u0)))
98+
p_prototype = zeros(eltype(u0), (length(u0), length(u0))) / oneunit(first(tspan))
9999
end
100100
# If a PDSFunction is to be evaluated and D is in-place, then d_prototype is used to store
101101
# evaluations of D.
102-
d_prototype = similar(u0)
102+
d_prototype = similar(u0 ./ oneunit(first(tspan)))
103103

104104
PD = PDSFunction{iip}(P, D; p_prototype, d_prototype,
105105
analytic, std_rhs)
@@ -121,7 +121,7 @@ end
121121
# Most specific constructor for PDSFunction
122122
function PDSFunction{iip, FullSpecialize}(P, D;
123123
p_prototype = nothing,
124-
d_prototype,
124+
d_prototype = nothing,
125125
analytic = nothing,
126126
std_rhs = nothing) where {iip}
127127
if std_rhs === nothing
@@ -143,11 +143,22 @@ function (PD::PDSFunction)(du, u, p, t)
143143
end
144144

145145
# Default implementation of the standard right-hand side evaluation function
146-
struct PDSStdRHS{P, D, PrototypeP, PrototypeD} <: Function
146+
struct PDSStdRHS{P, D, PrototypeP, PrototypeD, TMP} <: Function
147147
p::P
148148
d::D
149149
p_prototype::PrototypeP
150150
d_prototype::PrototypeD
151+
tmp::TMP
152+
end
153+
154+
function PDSStdRHS(P, D, p_prototype, d_prototype)
155+
if p_prototype isa AbstractSparseMatrix
156+
tmp = zeros(eltype(p_prototype), (size(p_prototype, 1),)) /
157+
oneunit(first(p_prototype)) # drop units
158+
else
159+
tmp = nothing
160+
end
161+
PDSStdRHS(P, D, p_prototype, d_prototype, tmp)
151162
end
152163

153164
# Evaluation of a PDSStdRHS (out-of-place)
@@ -163,9 +174,10 @@ function (PD::PDSStdRHS)(du, u, p, t)
163174
PD.p(PD.p_prototype, u, p, t)
164175

165176
if PD.p_prototype isa AbstractSparseMatrix
166-
# Same result but more efficient - at least currently for SparseMatrixCSC
167-
fill!(PD.d_prototype, one(eltype(PD.d_prototype)))
168-
mul!(vec(du), PD.p_prototype, PD.d_prototype)
177+
# row sum coded as matrix-vector product
178+
fill!(PD.tmp, one(eltype(PD.tmp)))
179+
mul!(vec(du), PD.p_prototype, PD.tmp)
180+
169181
for i in 1:length(u) #vec(du) .+= diag(PD.p_prototype)
170182
du[i] += PD.p_prototype[i, i]
171183
end
@@ -272,7 +284,7 @@ function ConservativePDSProblem{iip}(P, u0, tspan, p = NullParameters();
272284

273285
# p_prototype is used to store evaluations of P, if P is in-place.
274286
if isnothing(p_prototype) && iip
275-
p_prototype = zeros(eltype(u0), (length(u0), length(u0)))
287+
p_prototype = zeros(eltype(u0), (length(u0), length(u0))) / oneunit(first(tspan))
276288
end
277289

278290
PD = ConservativePDSFunction{iip}(P; p_prototype, analytic, std_rhs)
@@ -314,27 +326,30 @@ function (PD::ConservativePDSFunction)(du, u, p, t)
314326
end
315327

316328
# Default implementation of the standard right-hand side evaluation function
317-
struct ConservativePDSStdRHS{P, PrototypeP, TMP} <: Function
329+
struct ConservativePDSStdRHS{P, PrototypeP, TMP, TMP2} <: Function
318330
p::P
319331
p_prototype::PrototypeP
320332
tmp::TMP
333+
tmp2::TMP2
321334
end
322335

323336
function ConservativePDSStdRHS(P, p_prototype)
324337
if p_prototype isa AbstractSparseMatrix
325338
tmp = zeros(eltype(p_prototype), (size(p_prototype, 1),))
339+
tmp2 = tmp / oneunit(first(tmp)) # drop units
326340
else
327341
tmp = nothing
342+
tmp2 = nothing
328343
end
329-
ConservativePDSStdRHS(P, p_prototype, tmp)
344+
ConservativePDSStdRHS(P, p_prototype, tmp, tmp2)
330345
end
331346

332347
# Evaluation of a ConservativePDSStdRHS (out-of-place)
333348
function (PD::ConservativePDSStdRHS)(u, p, t)
334349
#vec(sum(PD.p(u, p, t), dims = 2)) - vec(sum(PD.p(u, p, t), dims = 1))
335350
P = PD.p(u, p, t)
336351

337-
f = zero(u)
352+
f = zeros(eltype(P), size(u))
338353
@fastmath @inbounds @simd for I in CartesianIndices(P)
339354
if !iszero(P[I])
340355
f[I[1]] += P[I]
@@ -347,8 +362,8 @@ end
347362
function (PD::ConservativePDSStdRHS)(u::SVector, p, t)
348363
P = PD.p(u, p, t)
349364

350-
f = similar(u) #constructs MVector
351-
zeroT = zero(eltype(u))
365+
f = similar(P[:, 1]) #constructs MVector
366+
zeroT = zero(eltype(P))
352367
for i in eachindex(f)
353368
f[i] = zeroT
354369
end
@@ -366,32 +381,37 @@ end
366381
# Evaluation of a ConservativePDSStdRHS (in-place)
367382
function (PD::ConservativePDSStdRHS)(du, u, p, t)
368383
PD.p(PD.p_prototype, u, p, t)
369-
sum_terms!(du, PD.tmp, PD.p_prototype)
384+
sum_terms!(du, PD.tmp, PD.tmp2, PD.p_prototype)
370385
return nothing
371386
end
372387

373388
# Generic fallback (for dense arrays)
374389
# This implementation does not need any auxiliary vectors
375-
@inline function sum_terms!(du, tmp, P)
376-
for i in 1:length(du)
390+
@inline function sum_terms!(du, tmp, tmp2, P)
391+
for i in eachindex(du)
377392
du[i] = zero(eltype(du))
378-
for j in 1:length(du)
393+
for j in eachindex(du)
379394
du[i] += P[i, j] - P[j, i]
380395
end
381396
end
382397
return nothing
383398
end
384399

385400
# Same result but more efficient - at least currently for SparseMatrixCSC
386-
@inline function sum_terms!(du, tmp, P::AbstractSparseMatrix)
387-
fill!(tmp, one(eltype(tmp)))
388-
mul!(vec(du), P, tmp)
401+
@inline function sum_terms!(du, tmp, tmp2, P::AbstractSparseMatrix)
402+
# row sum coded as matrix vector product
403+
fill!(tmp2, one(eltype(tmp2)))
404+
mul!(vec(du), P, tmp2)
405+
#sum!(vec(du), P)
406+
407+
#column sum
389408
sum!(tmp', P)
409+
390410
vec(du) .-= tmp
391411
return nothing
392412
end
393413

394-
@inline function sum_terms!(du, tmp, P::Tridiagonal)
414+
@inline function sum_terms!(du, tmp, tmp2, P::Tridiagonal)
395415
Base.require_one_based_indexing(du, P.dl, P.du)
396416
@assert length(du) == length(P.dl) + 1 == length(P.du) + 1
397417

src/sspmprk.jl

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -466,8 +466,9 @@ You can optionally choose the linear solver to be used by passing an
466466
algorithm from [LinearSolve.jl](https://github.com/SciML/LinearSolve.jl)
467467
as keyword argument `linsolve`.
468468
You can also choose the parameter `small_constant` which is added to all Patankar-weight denominators
469-
to avoid divisions by zero. You can pass a value explicitly, otherwise `small_constant` is set to
470-
`floatmin` of the floating point type used.
469+
to avoid divisions by zero. To display the default value for data type `type` evaluate
470+
`SSPMPRK43. small_constant_function(type)`, where `type` can be, e.g.,
471+
`Float64`.
471472
472473
The current implementation only supports fixed time steps.
473474
@@ -488,10 +489,26 @@ struct SSPMPRK43{F, T} <: OrdinaryDiffEqAlgorithm
488489
small_constant_function::T
489490
end
490491

491-
function SSPMPRK43(; linsolve = LUFactorization(), small_constant = 1e-50)
492-
if isnothing(small_constant)
493-
small_constant_function = floatmin
494-
elseif small_constant isa Number
492+
function small_constant_function_SSPMPRK43(type)
493+
if type == Float64
494+
small_constant = 1e-50
495+
elseif type == Float32
496+
# small_constant is chosen such that the problem below
497+
# (zero initial condition) can be solved
498+
# P_linmod(u, p, t) = [0 u[2]; 5*u[1] 0]
499+
# u0 = [1.0f0, 0.0f0]
500+
# prob = ConservativePDSProblem(P_linmod, u0, (0.0f0, 2.0f0))
501+
# sol = solve(prob, SSPMPRK43(); dt=0.1f0)
502+
small_constant = 1.0f-8
503+
else
504+
small_constant = floatmin(type)
505+
end
506+
return small_constant
507+
end
508+
509+
function SSPMPRK43(; linsolve = LUFactorization(),
510+
small_constant = small_constant_function_SSPMPRK43)
511+
if small_constant isa Number
495512
small_constant_function = Returns(small_constant)
496513
else # assume small_constant isa Function
497514
small_constant_function = small_constant
@@ -534,8 +551,7 @@ function get_constant_parameters(alg::SSPMPRK43)
534551
c3 = β20 + α21 * β10 + β21
535552

536553
return n1, n2, z, η1, η2, η3, η4, η5, η6, s, α10, α20, α21, α30, α31, α32, β10, β20,
537-
β21, β30,
538-
β31, β32, c3
554+
β21, β30, β31, β32, c3
539555
end
540556

541557
struct SSPMPRK43ConstantCache{T} <: OrdinaryDiffEqConstantCache
@@ -573,11 +589,12 @@ function alg_cache(alg::SSPMPRK43, u, rate_prototype, ::Type{uEltypeNoUnits},
573589
if !(f isa PDSFunction || f isa ConservativePDSFunction)
574590
throw(ArgumentError("SSPMPRK43 can only be applied to production-destruction systems"))
575591
end
576-
n1, n2, z, η1, η2, η3, η4, η5, η6, s, α10, α20, α21, α30, α31, α32, β10, β20, β21, β30, β31, β32, c3 = get_constant_parameters(alg)
592+
const_param = get_constant_parameters(alg)
593+
const_param = convert.(uEltypeNoUnits, const_param)
594+
n1, n2, z, η1, η2, η3, η4, η5, η6, s, α10, α20, α21, α30, α31, α32, β10, β20, β21, β30, β31, β32, c3 = const_param
595+
small_constant = alg.small_constant_function(uEltypeNoUnits)
577596
SSPMPRK43ConstantCache(n1, n2, z, η1, η2, η3, η4, η5, η6, s, α10, α20, α21, α30, α31,
578-
α32, β10,
579-
β20, β21, β30,
580-
β31, β32, c3, alg.small_constant_function(uEltypeNoUnits))
597+
α32, β10, β20, β21, β30, β31, β32, c3, small_constant)
581598
end
582599

583600
function initialize!(integrator, cache::SSPMPRK43ConstantCache)

test/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
99
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1010
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1111
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
12+
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
1213

1314
[compat]
1415
Aqua = "0.7, 0.8"
@@ -18,3 +19,4 @@ OrdinaryDiffEq = "6.59"
1819
Plots = "1.11.1"
1920
StaticArrays = "1.5"
2021
Statistics = "1"
22+
Unitful = "1.21"

0 commit comments

Comments
 (0)