diff --git a/Project.toml b/Project.toml index c814f6a..429a984 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "OrdinaryDiffEqOperatorSplitting" uuid = "760fc936-9fa0-4281-9c1a-468957eedc89" -version = "0.4.3" +version = "0.4.4" authors = ["Dennis Ogiermann and contributors"] [deps] diff --git a/src/OrdinaryDiffEqOperatorSplitting.jl b/src/OrdinaryDiffEqOperatorSplitting.jl index 773e179..878d400 100644 --- a/src/OrdinaryDiffEqOperatorSplitting.jl +++ b/src/OrdinaryDiffEqOperatorSplitting.jl @@ -146,6 +146,16 @@ end _inner_verbose(verbose) = verbose _process_verbose(verbose) = verbose +""" + inner_verbosity(verbose) + +The verbosity a leaf integrator under a splitting node runs with: the `inner_verbosity` +of an [`OperatorSplittingVerbosity`](@ref), or `verbose` itself where it already is a +leaf setting. A leaf integrator built from `config.values.verbose` passes it through +here; the splitting toggles live on the node, not on its leaves. +""" +inner_verbosity(verbose) = _inner_verbose(verbose) + # `verbose` reaches us either as a Bool or, through DiffEqBase v7's `init`, as a # verbosity specifier whose first type parameter is the on/off flag. Neither can be # used in a boolean context directly. diff --git a/src/config_tree.jl b/src/config_tree.jl index 2d53472..26513c3 100644 --- a/src/config_tree.jl +++ b/src/config_tree.jl @@ -429,9 +429,17 @@ const SPLIT_OPTION_KEYS = ( function split_integrator_options(values::NamedTuple) inner = inner_values(values) known = filter(in(SPLIT_OPTION_KEYS), keys(inner)) - return IntegratorOptions(; - verbose = values.verbose, - adaptive = values.adaptive, - NamedTuple{known}(inner)... + opts = NamedTuple{known}(inner) + # `dtmin` and `dtmax` share the node's time type: a `Float32` time span with the + # default `dtmin` would otherwise leave the two with no common type parameter. + T = typeof(values.dt) + opts = merge( + opts, (; + dtmin = T(get(opts, :dtmin, default_dtmin(T))), + dtmax = T(get(opts, :dtmax, typemax(T))), + ) ) + return IntegratorOptions(; verbose = values.verbose, adaptive = values.adaptive, opts...) end +default_dtmin(::Type{T}) where {T <: AbstractFloat} = eps(T) +default_dtmin(::Type{T}) where {T} = zero(T) diff --git a/src/integrator.jl b/src/integrator.jl index 7663e84..d4b20e4 100644 --- a/src/integrator.jl +++ b/src/integrator.jl @@ -8,8 +8,8 @@ IntegratorStats() = IntegratorStats(0, 0) Base.@kwdef mutable struct IntegratorOptions{tType, fType, vbType, F3, atolType, rtolType, normType} adaptive::Bool - dtmin::tType = eps(Float64) - dtmax::tType = Inf + dtmin::tType + dtmax::tType failfactor::fType = 4.0 verbose::vbType = DEFAULT_VERBOSITY isoutofdomain::F3 = DiffEqBase.ODE_DEFAULT_ISOUTOFDOMAIN diff --git a/test/precision.jl b/test/precision.jl new file mode 100644 index 0000000..830708b --- /dev/null +++ b/test/precision.jl @@ -0,0 +1,28 @@ +using OrdinaryDiffEqLowOrderRK +using OrdinaryDiffEqOperatorSplitting +using Test + +@testset "Float32 time span" begin + f(du, u, p, t) = @. du = -u + split_f = GenericSplitFunction((ODEFunction(f),), (1:1,)) + prob = OperatorSplittingProblem(split_f, Float32[1.0], (0.0f0, 1.0f0)) + alg = LieTrotterGodunov((Euler(),)) + + integrator = init(prob, alg; dt = 0.1f0) + @test integrator.opts.dtmin === eps(Float32) + @test integrator.opts.dtmax === 1.0f0 + step!(integrator) + @test integrator.t === 0.1f0 + @test integrator.u ≈ Float32[0.9] + + # An explicitly given bound is converted to the time type. + integrator = init(prob, alg; dt = 0.1f0, dtmin = 1.0e-3) + @test integrator.opts.dtmin === 1.0f-3 +end + +@testset "inner_verbosity" begin + @test OrdinaryDiffEqOperatorSplitting.inner_verbosity(true) isa OrdinaryDiffEqOperatorSplitting.DiffEqBase.DEVerbosity + v = OrdinaryDiffEqOperatorSplitting.OperatorSplittingVerbosity(OrdinaryDiffEqOperatorSplitting.SciMLLogging.Standard()) + @test OrdinaryDiffEqOperatorSplitting.inner_verbosity(v) === v.inner_verbosity + @test OrdinaryDiffEqOperatorSplitting.inner_verbosity(v.inner_verbosity) === v.inner_verbosity +end