Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better error message if a ReactionMethod fails at runtime, including @generated dispatch #139

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 83 additions & 31 deletions src/Model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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(
Expand All @@ -792,67 +799,112 @@ 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},
deltat::Float64=0.0
deltat::Float64=0.0,
) where {M, V, C}

# Write out unrolled loop as an expression
# See https://discourse.julialang.org/t/manually-unroll-operations-with-objects-of-tuple/11604

ex = quote ; end # empty expression
for j=1:fieldcount(M)
push!(ex.args,
unrollex = quote ; end # empty expression
for i=1:fieldcount(M)
push!(unrollex.args,
quote
lasti = $i
# 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
)
)
end

# interpolate the unrolled loop into a try-catch
ex = quote
lasti = -1

try
$unrollex
catch
lasti != -1 && _dispatch_methodlist_methoderror(dl.methods[lasti][])
rethrow()
end

return nothing
end
push!(ex.args, quote; return nothing; end)

return ex
end


@generated function dispatch_methodlist(
dl::ReactionMethodDispatchList{M, V, C},
pa::ParameterAggregator,
deltat::Float64=0.0
) 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)
push!(ex.args,
# Write out unrolled loop as an expression
# See https://discourse.julialang.org/t/manually-unroll-operations-with-objects-of-tuple/11604
unrollex = quote ; end # empty expression
for i=1:fieldcount(M)
push!(unrollex.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)
lasti = $i
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
)
end
push!(ex.args, quote; return nothing; end)

# interpolate the unrolled loop into a try-catch
ex = quote
lasti = -1

try
$unrollex
catch
lasti != -1 && _dispatch_methodlist_methoderror(dl.methods[lasti][])
rethrow()
end

return nothing
end

return ex
end

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

#################################
# Pretty printing
################################
Expand Down
26 changes: 16 additions & 10 deletions src/Reaction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down Expand Up @@ -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

"""
Expand Down
17 changes: 15 additions & 2 deletions src/ReactionMethod.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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
Loading