diff --git a/src/systems/diffeqs/abstractodesystem.jl b/src/systems/diffeqs/abstractodesystem.jl index 01f847d5d6..f686ebcede 100644 --- a/src/systems/diffeqs/abstractodesystem.jl +++ b/src/systems/diffeqs/abstractodesystem.jl @@ -1504,5 +1504,5 @@ function InitializationProblem{iip, specialize}(sys::AbstractSystem, else NonlinearLeastSquaresProblem end - TProb(isys, u0map, parammap; kwargs..., build_initializeprob = false) + TProb(isys, u0map, parammap; kwargs..., build_initializeprob = false, is_initializeprob = true) end diff --git a/src/systems/problem_utils.jl b/src/systems/problem_utils.jl index 93a0749acb..e9b836ddf7 100644 --- a/src/systems/problem_utils.jl +++ b/src/systems/problem_utils.jl @@ -325,6 +325,24 @@ function Base.showerror(io::IO, err::UnexpectedSymbolicValueInVarmap) """) end +struct MissingGuessError <: Exception + syms::Vector{Any} + vals::Vector{Any} +end + +function Base.showerror(io::IO, err::MissingGuessError) + println(io, + """ + Cyclic guesses detected in the system. Symbolic values were found for the following variables/parameters in the map: \ + """) + for (sym, val) in zip(err.syms, err.vals) + println(io, "$sym => $val") + end + println(io, + """ + In order to resolve this, please provide additional numeric guesses so that the chain can be resolved to assign numeric values to each variable. """) +end + """ $(TYPEDSIGNATURES) @@ -342,10 +360,11 @@ Keyword arguments: [`missingvars`](@ref) to perform the check. - `allow_symbolic` allows the returned array to contain symbolic values. If this is `true`, `promotetoconcrete` is set to `false`. +- `is_initializeprob, guesses`: Used to determine whether the system is missing guesses. """ function better_varmap_to_vars(varmap::AbstractDict, vars::Vector; tofloat = true, use_union = true, container_type = Array, - toterm = default_toterm, promotetoconcrete = nothing, check = true, allow_symbolic = false) + toterm = default_toterm, promotetoconcrete = nothing, check = true, allow_symbolic = false, is_initializeprob = false) isempty(vars) && return nothing if check @@ -354,9 +373,17 @@ function better_varmap_to_vars(varmap::AbstractDict, vars::Vector; end vals = map(x -> varmap[x], vars) if !allow_symbolic + missingsyms = Any[] + missingvals = Any[] for (sym, val) in zip(vars, vals) symbolic_type(val) == NotSymbolic() && continue - throw(UnexpectedSymbolicValueInVarmap(sym, val)) + push!(missingsyms, sym) + push!(missingvals, val) + end + + if !isempty(missingsyms) + is_initializeprob ? throw(MissingGuessError(missingsyms, missingvals)) : + throw(UnexpectedSymbolicValueInVarmap(missingsyms[1], missingvals[1])) end end @@ -704,7 +731,7 @@ Keyword arguments: - `fully_determined`: Override whether the initialization system is fully determined. - `check_initialization_units`: Enable or disable unit checks when constructing the initialization problem. -- `tofloat`, `use_union`: Passed to [`better_varmap_to_vars`](@ref) for building `u0` (and +- `tofloat`, `use_union`, `is_initializeprob`: Passed to [`better_varmap_to_vars`](@ref) for building `u0` (and possibly `p`). - `u0_constructor`: A function to apply to the `u0` value returned from `better_varmap_to_vars` to construct the final `u0` value. @@ -742,7 +769,7 @@ function process_SciMLProblem( circular_dependency_max_cycles = 10, substitution_limit = 100, use_scc = true, force_initialization_time_independent = false, algebraic_only = false, - allow_incomplete = false, kwargs...) + allow_incomplete = false, is_initializeprob = false, kwargs...) dvs = unknowns(sys) ps = parameters(sys; initial_parameters = true) iv = has_iv(sys) ? get_iv(sys) : nothing @@ -815,7 +842,7 @@ function process_SciMLProblem( u0 = better_varmap_to_vars( op, dvs; tofloat = true, use_union = false, - container_type = u0Type, allow_symbolic = symbolic_u0) + container_type = u0Type, allow_symbolic = symbolic_u0, is_initializeprob) if u0 !== nothing u0 = u0_constructor(u0) diff --git a/src/variables.jl b/src/variables.jl index ddc33fa838..3f070fea3c 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -219,7 +219,7 @@ end function Base.showerror(io::IO, e::MissingVariablesError) println(io, MISSING_VARIABLES_MESSAGE) - println(io, e.vars) + println(io, join(e.vars, ", ")) end function _varmap_to_vars(varmap::Dict, varlist; defaults = Dict(), check = false, diff --git a/test/initial_values.jl b/test/initial_values.jl index 2db552c3ba..66230557ab 100644 --- a/test/initial_values.jl +++ b/test/initial_values.jl @@ -172,7 +172,7 @@ end [p => 2q, q => 3p]; warn_cyclic_dependency = true) catch end - @test_throws ModelingToolkit.UnexpectedSymbolicValueInVarmap ODEProblem( + @test_throws ModelingToolkit.MissingGuessError ODEProblem( sys, [x => 1, y => 2], (0.0, 1.0), [p => 2q, q => 3p]) end @@ -199,3 +199,21 @@ end @test SciMLBase.successful_retcode(sol) @test sol.u[1] ≈ [1.0, 1.0, 0.5, 0.5] end + +@testset "Missing/cyclic guesses throws error" begin + @parameters g + @variables x(t) y(t) [state_priority = 10] λ(t) + eqs = [D(D(x)) ~ λ * x + D(D(y)) ~ λ * y - g + x^2 + y^2 ~ 1] + @mtkbuild pend = ODESystem(eqs, t) + + @test_throws ModelingToolkit.MissingGuessError ODEProblem(pend, [x => 1], (0, 1), [g => 1], guesses = [y => λ, λ => y + 1]) + ODEProblem(pend, [x => 1], (0, 1), [g => 1], guesses = [y => λ, λ => 0.5]) + + # Throw multiple if multiple are missing + @variables a(t) b(t) c(t) d(t) e(t) + eqs = [D(a) ~ b, D(b) ~ c, D(c) ~ d, D(d) ~ e, D(e) ~ 1] + @mtkbuild sys = ODESystem(eqs, t) + @test_throws ["a(t)", "c(t)"] ODEProblem(sys, [e => 2, a => b, b => a + 1, c => d, d => c + 1], (0, 1)) +end