diff --git a/src/ParamEstim.jl b/src/ParamEstim.jl index 6d53081f..866686d5 100644 --- a/src/ParamEstim.jl +++ b/src/ParamEstim.jl @@ -138,7 +138,7 @@ end (cf::RSSCostFunction)(params::AbstractVector) = cf(candidate(cf, params)) _solve(cf::RSSCostFunction, params::AbstractVector) = _solve(cf, cf._func(params)) -_solve(::RSSCostFunction, prob::Problem) = solve(prob) +_solve(::RSSCostFunction, prob::Problem) = solve(prob, verbose = false) _solve(::RSSCostFunction, sol::Solution) = sol """ diff --git a/src/finite.jl b/src/finite.jl index d378f145..4b9f9e22 100644 --- a/src/finite.jl +++ b/src/finite.jl @@ -161,6 +161,7 @@ Uses backward Euler time discretization and a second-order central difference sc # Keyword arguments - `abstol=1e-3`: nonlinear solver tolerance. +- `verbose=true`: whether warnings are emitted if solving is unsuccessful. --- @@ -174,13 +175,15 @@ Solve the `DirichletProblem` `prob` with a finite-difference scheme. # Keyword arguments - `abstol=1e-3`: nonlinear solver tolerance. +- `verbose=true`: whether warnings are emitted if solving is unsuccessful. """ function solve(prob::Union{ DirichletProblem{<:DiffusionEquation{1}}, FiniteProblem{<:DiffusionEquation{1}}, }, alg::FiniteDifference; - abstol = 1e-3) + abstol = 1e-3, + verbose = true) if prob isa DirichletProblem @argcheck iszero(prob.ob) "FiniteDifference only supports fixed boundaries" @argcheck isnothing(alg._pre) "pre not valid for a DirichletProblem (use BoltzmannODE directly instead)" @@ -204,7 +207,8 @@ function solve(prob::Union{ prob.i isa Number presol = solve(DirichletProblem(prob.eq, i = prob.i, b = prob.b), alg._pre, - abstol = abstol) + abstol = abstol, + verbose = verbose) if presol.retcode != ReturnCode.Success θ .= prob.i return FiniteSolution(r, @@ -348,8 +352,8 @@ function solve(prob::Union{ end end -function solve(prob::FiniteProblem{<:DiffusionEquation{1}}; abstol = 1e-3) - solve(prob, FiniteDifference(pre = BoltzmannODE()), abstol = abstol) +function solve(prob::FiniteProblem{<:DiffusionEquation{1}}; abstol = 1e-3, verbose = true) + solve(prob, FiniteDifference(pre = BoltzmannODE()), abstol = abstol, verbose = verbose) end """ diff --git a/src/integration.jl b/src/integration.jl index 4564005a..2c9d936f 100644 --- a/src/integration.jl +++ b/src/integration.jl @@ -55,7 +55,11 @@ monotonicity(odeprob::ODEProblem)::Int = sign(odeprob.u0[2]) const _ODE_ALG = RadauIIA5() const _ODE_MAXITERS = 1000 -function _init(odeprob::ODEProblem, alg::BoltzmannODE; i = nothing, abstol = 0) +function _init(odeprob::ODEProblem, + ::BoltzmannODE; + i = nothing, + abstol = 0, + verbose = true) if !isnothing(i) direction = monotonicity(odeprob) @@ -70,17 +74,18 @@ function _init(odeprob::ODEProblem, alg::BoltzmannODE; i = nothing, abstol = 0) _ODE_ALG, maxiters = _ODE_MAXITERS, callback = past_limit, - verbose = false) + verbose = verbose) end - return init(odeprob, _ODE_ALG, maxiters = _ODE_MAXITERS, verbose = false) + return init(odeprob, _ODE_ALG, maxiters = _ODE_MAXITERS, verbose = verbose) end function _init(prob::Union{CauchyProblem, SorptivityProblem}, alg::BoltzmannODE; i = nothing, - abstol = 0) - _init(boltzmann(prob), alg, i = i, abstol = abstol) + abstol = 0, + verbose = true) + _init(boltzmann(prob), alg, i = i, abstol = abstol, verbose = verbose) end function _reinit!(integrator, prob::CauchyProblem) @@ -96,8 +101,8 @@ function _reinit!(integrator, prob::SorptivityProblem) end """ - solve(prob::CauchyProblem[, alg::BoltzmannODE]) -> Solution - solve(prob::SorptivityProblem[, alg::BoltzmannODE]) -> Solution + solve(prob::CauchyProblem[, alg::BoltzmannODE; verbose]) -> Solution + solve(prob::SorptivityProblem[, alg::BoltzmannODE; verbose]) -> Solution Solve the problem `prob`. @@ -105,6 +110,9 @@ Solve the problem `prob`. - `prob`: problem to solve. - `alg=BoltzmannODE()`: algorithm to use. +# Keyword arguments +- `verbose=true`: whether warnings are emitted if solving is unsuccessful. + # References GERLERO, G. S.; BERLI, C. L. A.; KLER, P. A. Open-source high-performance software packages for direct and inverse solving of horizontal capillary flow. Capillarity, 2023, vol. 6, no. 2, p. 31-40. @@ -113,8 +121,8 @@ See also: [`Solution`](@ref), [`BoltzmannODE`](@ref) """ function solve(prob::Union{CauchyProblem, SorptivityProblem}, alg::BoltzmannODE = BoltzmannODE(); - abstol = 1e-3) - odesol = solve!(_init(prob, alg, abstol = abstol)) + verbose = true) + odesol = solve!(_init(prob, alg, verbose = verbose)) @assert odesol.retcode != ReturnCode.Success diff --git a/src/shooting.jl b/src/shooting.jl index d7dd85b7..1a84f3c1 100644 --- a/src/shooting.jl +++ b/src/shooting.jl @@ -21,13 +21,13 @@ end function _init(prob::DirichletProblem, alg::BoltzmannODE; d_dob, abstol) return _init(CauchyProblem(prob.eq, b = prob.b, d_dob = d_dob, ob = prob.ob), alg, - i = prob.i, abstol = abstol) + i = prob.i, abstol = abstol, verbose = false) end function _shoot!(integrator, prob::DirichletProblem; d_dob, abstol) return _shoot!(integrator, - CauchyProblem(prob.eq, b = prob.b, d_dob = d_dob, ob = prob.ob), - i = prob.i, abstol = abstol) + CauchyProblem(prob.eq, b = prob.b, d_dob = d_dob, ob = prob.ob), i = prob.i, + abstol = abstol) end """ @@ -42,6 +42,7 @@ Solve the problem `prob`. # Keyword arguments - `abstol=1e-3`: absolute tolerance for the initial condition. - `maxiters=100`: maximum number of iterations. +- `verbose=true`: whether warnings are emitted if solving is unsuccessful. # References GERLERO, G. S.; BERLI, C. L. A.; KLER, P. A. Open-source high-performance software packages for direct and inverse solving of horizontal capillary flow. @@ -51,7 +52,8 @@ See also: [`Solution`](@ref), [`BoltzmannODE`](@ref) """ function solve(prob::DirichletProblem, alg::BoltzmannODE = BoltzmannODE(); abstol = 1e-3, - maxiters = 100) + maxiters = 100, + verbose = true) @argcheck abstol ≥ zero(abstol) @argcheck maxiters ≥ 0 @@ -71,6 +73,9 @@ function solve(prob::DirichletProblem, alg::BoltzmannODE = BoltzmannODE(); @assert integrator.sol.retcode != ReturnCode.Success retcode = integrator.sol.retcode == ReturnCode.Terminated ? ReturnCode.Success : integrator.sol.retcode + if verbose && !SciMLBase.successful_retcode(integrator.sol) + @warn "Problem has a trivial solution but failed to obtain it" + end return Solution(integrator.sol, prob, alg, _retcode = retcode, _niter = 0) end @@ -90,6 +95,9 @@ function solve(prob::DirichletProblem, alg::BoltzmannODE = BoltzmannODE(); end end + if verbose + @warn "Maximum number of iterations reached without convergence" + end return Solution(integrator.sol, prob, alg, @@ -103,7 +111,8 @@ function _init(prob::FlowrateProblem, alg::BoltzmannODE; b, abstol) return _init(SorptivityProblem(prob.eq, b = b, S = 2prob.Qb / prob._αh / ob, ob = ob), alg, i = prob.i, - abstol = abstol) + abstol = abstol, + verbose = false) end function _shoot!(integrator, prob::FlowrateProblem; b, abstol) @@ -126,6 +135,7 @@ Solve the problem `prob`. # Keyword arguments - `abstol=1e-3`: absolute tolerance for the initial condition. - `maxiters=100`: maximum number of iterations. +- `verbose=true`: whether warnings are emitted if solving is unsuccessful. # References GERLERO, G. S.; BERLI, C. L. A.; KLER, P. A. Open-source high-performance software packages for direct and inverse solving of horizontal capillary flow. @@ -135,7 +145,8 @@ See also: [`Solution`](@ref), [`BoltzmannODE`](@ref) """ function solve(prob::FlowrateProblem; alg::BoltzmannODE = BoltzmannODE(), abstol = 1e-3, - maxiters = 100) + maxiters = 100, + verbose = true) @argcheck abstol ≥ zero(abstol) @argcheck maxiters ≥ 0 @@ -153,10 +164,16 @@ function solve(prob::FlowrateProblem; alg::BoltzmannODE = BoltzmannODE(), if monotonicity(prob) == 0 integrator, resid = _shoot!(integrator, prob, b = prob.i, abstol = abstol) @assert iszero(resid) + @assert integrator.sol.retcode != ReturnCode.Success + retcode = integrator.sol.retcode == ReturnCode.Terminated ? ReturnCode.Success : + integrator.sol.retcode + if verbose && !SciMLBase.successful_retcode(integrator.sol) + @warn "Problem has a trivial solution but failed to obtain it" + end return Solution(integrator.sol, prob, alg, - _retcode = ReturnCode.Success, + _retcode = retcode, _niter = 0) end @@ -174,6 +191,9 @@ function solve(prob::FlowrateProblem; alg::BoltzmannODE = BoltzmannODE(), end end + if verbose + @warn "Maximum number of iterations reached without convergence" + end return Solution(integrator.sol, prob, alg, diff --git a/test/test_dirichlet.jl b/test/test_dirichlet.jl index fea7dec1..c18f1390 100644 --- a/test/test_dirichlet.jl +++ b/test/test_dirichlet.jl @@ -121,17 +121,8 @@ @testset "unsolved" begin prob = DirichletProblem(identity, i = 0, b = 1) - θ = solve(prob, maxiters = 0) + θ = @test_logs (:warn, "Maximum number of iterations reached without convergence") solve(prob, + maxiters = 0) @test θ.retcode == ReturnCode.MaxIters end - - @testset "unsolvable" begin - prob = DirichletProblem(identity, i = -1e-3, b = 1) - θ = solve(prob) - @test_broken θ.retcode == ReturnCode.Unstable - - prob = DirichletProblem(identity, i = 0, b = -1) - θ = solve(prob) - @test_broken θ.retcode == ReturnCode.Unstable - end end