From 2879027daf297ba7775052706268cadc1202446d Mon Sep 17 00:00:00 2001 From: Stuart Daines Date: Thu, 28 Nov 2024 14:23:22 +0000 Subject: [PATCH] Better error message if a ReactionMethod fails at runtime Catch runtime exception if a ReactionMethod fails, and print an error message containing tje name of the failed Reaction as in the YAML config file. This is helpful if there are multiple instances of the same Reaction and it's not clear which one is failing. NB: requires 'generated_dispatch=false' argument to PALEOmodel.initialize! --- src/Model.jl | 103 ++++++++++++++++++++++++++++++++---------- src/Reaction.jl | 26 +++++++---- src/ReactionMethod.jl | 17 ++++++- 3 files changed, 110 insertions(+), 36 deletions(-) diff --git a/src/Model.jl b/src/Model.jl index 4d00edbe..65671071 100644 --- a/src/Model.jl +++ b/src/Model.jl @@ -246,7 +246,7 @@ function create_model_from_config( catch e error("$(typeof(e)) while reading .yaml config file(s) $(abspath.(config_files)).\n"* "If the error isn't obvious by looking at the file(s) (often this is a whitespace issue), "* - "try an online YAML validator eg http://www.yamllint.com") + "install the VS Code YAML plugin, or try an online YAML validator eg http://www.yamllint.com") end conf_model = data[configmodel] @@ -778,12 +778,19 @@ function dispatch_methodlist( dl::ReactionMethodDispatchListNoGen, deltat::Float64=0.0 ) + lasti = -1 - for i in eachindex(dl.methods) - call_method(dl.methods[i], dl.vardatas[i], dl.cellranges[i], deltat) - end + try + for i in eachindex(dl.methods) + lasti = i + call_method(dl.methods[i], dl.vardatas[i], dl.cellranges[i], deltat) + end + catch + lasti != -1 && _dispatch_methodlist_methoderror(dl.methods[lasti][]) + rethrow() + end - return nothing + return nothing end function dispatch_methodlist( @@ -792,33 +799,62 @@ function dispatch_methodlist( deltat::Float64=0.0 ) - for j in eachindex(dl.methods) - methodref = dl.methods[j] - if has_modified_parameters(pa, methodref) - call_method(methodref, get_parameters(pa, methodref), dl.vardatas[j], dl.cellranges[j], deltat) - else - call_method(methodref, dl.vardatas[j], dl.cellranges[j], deltat) + lasti = -1 + + try + for i in eachindex(dl.methods) + lasti = i + methodref = dl.methods[i] + if has_modified_parameters(pa, methodref) + call_method(methodref, get_parameters(pa, methodref), dl.vardatas[i], dl.cellranges[i], deltat) + else + call_method(methodref, dl.vardatas[i], dl.cellranges[i], deltat) + end end - end + catch + lasti != -1 && _dispatch_methodlist_methoderror(dl.methods[lasti][]) + rethrow() + end - return nothing + return nothing end -@generated function dispatch_methodlist( - dl::ReactionMethodDispatchList{M, V, C}, +function _dispatch_methodlist_methoderror(reactionmethod) + io = IOBuffer() + println(io, "dispatch_methodlist: a ReactionMethod failed:") + show(io, MIME"text/plain"(), reactionmethod) + @warn String(take!(io)) + return +end + +function dispatch_methodlist( + @nospecialize(dl::ReactionMethodDispatchList), deltat::Float64=0.0 +) + try + _dispatch_methodlist(dl, deltat) + catch + _dispatch_methodlist_nomethoderroravailable() + rethrow() + end + return nothing +end + +@generated function _dispatch_methodlist( + dl::ReactionMethodDispatchList{M, V, C}, + deltat::Float64, ) where {M, V, C} # See https://discourse.julialang.org/t/manually-unroll-operations-with-objects-of-tuple/11604 ex = quote ; end # empty expression - for j=1:fieldcount(M) + for i=1:fieldcount(M) push!(ex.args, quote # let - # call_method(dl.methods[$j][], dl.vardatas[$j][], dl.cellranges[$j], deltat) + # call_method(dl.methods[$i][], dl.vardatas[$i][], dl.cellranges[$i], deltat) # pass Ref to function to reduce compile time - call_method(dl.methods[$j], dl.vardatas[$j], dl.cellranges[$j], deltat) + call_method(dl.methods[$i], dl.vardatas[$i], dl.cellranges[$i], deltat) # end end ) @@ -828,22 +864,36 @@ end return ex end -@generated function dispatch_methodlist( +function dispatch_methodlist( + @nospecialize(dl::ReactionMethodDispatchList), + @nospecialize(pa::ParameterAggregator), + deltat::Float64=0.0 +) + try + _dispatch_methodlist(dl, pa, deltat) + catch + _dispatch_methodlist_nomethoderroravailable() + rethrow() + end + return nothing +end + +@generated function _dispatch_methodlist( dl::ReactionMethodDispatchList{M, V, C}, pa::ParameterAggregator, - deltat::Float64=0.0 + deltat::Float64 ) where {M, V, C} # See https://discourse.julialang.org/t/manually-unroll-operations-with-objects-of-tuple/11604 ex = quote ; end # empty expression - for j=1:fieldcount(M) + for i=1:fieldcount(M) push!(ex.args, quote - if has_modified_parameters(pa, dl.methods[$j]) - call_method(dl.methods[$j], get_parameters(pa, dl.methods[$j]), dl.vardatas[$j], dl.cellranges[$j], deltat) + if has_modified_parameters(pa, dl.methods[$i]) + call_method(dl.methods[$i], get_parameters(pa, dl.methods[$i]), dl.vardatas[$i], dl.cellranges[$i], deltat) else - call_method(dl.methods[$j], dl.vardatas[$j], dl.cellranges[$j], deltat) + call_method(dl.methods[$i], dl.vardatas[$i], dl.cellranges[$i], deltat) end end ) @@ -853,6 +903,11 @@ end return ex end +function _dispatch_methodlist_nomethoderroravailable() + @warn "dispatch_methodlist: a ReactionMethod failed. To get a more detailed error report "* + "including the YAML name of the failed Reaction, rerun with 'generated_dispatch=false' argument added to PALEOmodel.initialize!" +end + ################################# # Pretty printing ################################ diff --git a/src/Reaction.jl b/src/Reaction.jl index 82d47c10..be947cbf 100644 --- a/src/Reaction.jl +++ b/src/Reaction.jl @@ -324,11 +324,13 @@ end add_method_do!(@nospecialize(reaction::AbstractReaction), @nospecialize(methodfn::Function), @nospecialize(vars::Tuple{Vararg{AbstractVarList}}); kwargs...) = _add_method!(reaction, methodfn, vars, add_method_do!; kwargs...) +default_preparefn(m, vardata) = vardata + function _add_method!( @nospecialize(reaction::AbstractReaction), @nospecialize(methodfn::Function), @nospecialize(vars::Tuple{Vararg{AbstractVarList}}), add_method_fn; name=string(methodfn), p=nothing, - preparefn=(m, vardata) -> vardata, + preparefn=default_preparefn, operatorID=reaction.operatorID, domain=reaction.domain ) @@ -611,15 +613,19 @@ function Base.show(io::IO, react::AbstractReaction) end function Base.show(io::IO, ::MIME"text/plain", react::AbstractReaction) - println(io, typename(react)) - println(io, " name='", react.name, "'") - println(io, " classname='", react.classname, "'") - println(io, " domain='", domainname(react), "'") - println(io, " operatorID=", react.operatorID) - println(io, " parameters=", get_parameters(react)) - println(io, " methods_setup=", react.methods_setup) - println(io, " methods_initialize=", react.methods_initialize) - println(io, " methods_do=", react.methods_do) + dump_reaction(io, react) +end + +function dump_reaction(io::IO, react::AbstractReaction; prefix="", show_parameters::Bool=true) + println(io, prefix, typename(react)) + println(io, prefix, " name='", react.name, "'") + println(io, prefix, " classname='", react.classname, "'") + println(io, prefix, " domain='", domainname(react), "'") + println(io, prefix, " operatorID=", react.operatorID) + show_parameters && println(io, prefix, " parameters=", get_parameters(react)) + println(io, prefix, " methods_setup=", react.methods_setup) + println(io, prefix, " methods_initialize=", react.methods_initialize) + println(io, prefix, " methods_do=", react.methods_do) end """ diff --git a/src/ReactionMethod.jl b/src/ReactionMethod.jl index 94b92277..ac9647f1 100644 --- a/src/ReactionMethod.jl +++ b/src/ReactionMethod.jl @@ -143,7 +143,7 @@ call_method_codefn(io::IO, codefn, method::ReactionMethod{M, R, P, 5}, vardata, """ get_variables_tuple(method::AbstractReactionMethod) -> (Vector{VariableReaction}, ...) - + println(io, typename(react)) Get all [`VariableReaction`](@ref)s from `method` as a Tuple of `Vector{VariableReaction}` """ get_variables_tuple(@nospecialize(method::ReactionMethod); flatten=true) = Tuple(get_variables(vl; flatten) for vl in method.varlists) @@ -209,7 +209,7 @@ get_rate_stoichiometry(@nospecialize(m::ReactionMethod)) = [] # Pretty printing ############################################ -"compact form" +# compact form function Base.show(io::IO, @nospecialize(method::ReactionMethod)) print( io, @@ -220,3 +220,16 @@ function Base.show(io::IO, @nospecialize(method::ReactionMethod)) ")", ) end + +# multiline form +function Base.show(io::IO, ::MIME"text/plain", @nospecialize(method::ReactionMethod)) + println(io, "ReactionMethod") + println(io, " fullname='", fullname(method), "'") + println(io, " methodfn=", string(method.methodfn)) + println(io, " preparefn=", string(method.preparefn)) + println(io, " domain='", method.domain.name , "'") + println(io, " operatorID=", method.operatorID, "'") + + print(io, " reaction=") + dump_reaction(io, method.reaction; prefix=" ", show_parameters=false) +end \ No newline at end of file