Hello,
I am trying to make an interactive figure of a SIV system.
I have implemented a parameter vector with Parsers
so that each parameter is accessed by name. But how can I change such a vector with lift
from GLMakie?
The script below works until the sliders are not used (and the values not changed). Afterards I get the error:
Error in callback:
MethodError: no method matching setindex!(::@NamedTuple{μ::Float64, κ::Float64, ω::Float64, δ::Float64, η::Float64, β::Int64, λ::Int64}, ::Float64, ::Int64)
Stacktrace:
[1] (::var"#5#6")(val::Float64)
@ Main ~/Documents/Model/MkBact_1.jl:109
[2] #invokelatest#2
@ ./essentials.jl:892 [inlined]
[3] invokelatest
@ ./essentials.jl:889 [inlined]
[4] notify
@ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:206 [inlined]
[5] setindex!(observable::Observable, val::Any)
@ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:123
[6] (::Makie.var"#1890#1905"{Slider, Observable{Any}})(i::Int64)
@ Makie ~/.julia/packages/Makie/VRavR/src/makielayout/blocks/slider.jl:63
[7] #invokelatest#2
@ ./essentials.jl:892 [inlined]
[8] invokelatest
@ ./essentials.jl:889 [inlined]
[9] notify
@ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:206 [inlined]
[10] setindex!(observable::Observable, val::Any)
@ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:123
[11] (::Makie.var"#1896#1911"{Slider, Observable{Int64}, Observable{Vector{Point{2, Float32}}}, Observable{Any}})(event::MouseEvent)
@ Makie ~/.julia/packages/Makie/VRavR/src/makielayout/blocks/slider.jl:128
[12] (::Makie.var"#1371#1372"{Makie.var"#1896#1911"{Slider, Observable{Int64}, Observable{Vector{Point{2, Float32}}}, Observable{Any}}})(event::MouseEvent)
@ Makie ~/.julia/packages/Makie/VRavR/src/makielayout/mousestatemachine.jl:94
[13] #invokelatest#2
@ ./essentials.jl:892 [inlined]
[14] invokelatest
@ ./essentials.jl:889 [inlined]
[15] notify
@ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:206 [inlined]
[16] setindex!
@ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:123 [inlined]
[17] (::Makie.var"#1441#1443"{Scene, Base.RefValue{Bool}, Base.RefValue{Union{Nothing, Makie.Mouse.Button}}, Base.RefValue{Float64}, Base.RefValue{Float64}, Base.RefValue{Bool}, Base.RefValue{Bool}, Base.RefValue{Union{Nothing, Makie.Mouse.Button}}, Base.RefValue{Bool}, Base.RefValue{Point{2, Float32}}, Base.RefValue{Point{2, Float32}}, Base.RefValue{Makie.Mouse.Action}, Observable{MouseEvent}, Float64, Module})(event::Makie.MouseButtonEvent)
@ Makie ~/.julia/packages/Makie/VRavR/src/makielayout/mousestatemachine.jl:299
[18] #invokelatest#2
@ ./essentials.jl:892 [inlined]
[19] invokelatest
@ ./essentials.jl:889 [inlined]
[20] notify
@ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:206 [inlined]
[21] setindex!
@ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:123 [inlined]
[22] (::GLMakie.var"#mousebuttons#168"{Observable{Makie.MouseButtonEvent}})(window::GLFW.Window, button::GLFW.MouseButton, action::GLFW.Action, mods::Int32)
@ GLMakie ~/.julia/packages/GLMakie/QyIWu/src/events.jl:102
[23] _MouseButtonCallbackWrapper(window::GLFW.Window, button::GLFW.MouseButton, action::GLFW.Action, mods::Int32)
@ GLFW ~/.julia/packages/GLFW/BWxfF/src/callback.jl:43
[24] PollEvents
@ ~/.julia/packages/GLFW/BWxfF/src/glfw3.jl:620 [inlined]
[25] pollevents(screen::GLMakie.Screen{GLFW.Window})
@ GLMakie ~/.julia/packages/GLMakie/QyIWu/src/screen.jl:449
[26] on_demand_renderloop(screen::GLMakie.Screen{GLFW.Window})
@ GLMakie ~/.julia/packages/GLMakie/QyIWu/src/screen.jl:937
[27] renderloop(screen::GLMakie.Screen{GLFW.Window})
@ GLMakie ~/.julia/packages/GLMakie/QyIWu/src/screen.jl:963
[28] (::GLMakie.var"#69#70"{GLMakie.Screen{GLFW.Window}})()
@ GLMakie ~/.julia/packages/GLMakie/QyIWu/src/screen.jl:824
How can I properly change the parameters for the solver by name?
Here is the code I have written (courtesy Steffen Plunder):
using GLMakie, DifferentialEquations, Parsers
function odeSolver!(du, u, p, t)
μ = p.μ # bacterial growth rate
κ = p.κ # bacterial carrying capacity
ω = p.ω # system wash-out rate
δ = p.δ # phagial infection rate
η = p.η # phagial lysis rate (inverse latency)
β = p.β # phagial burst size
λ = p.λ # phagial decay rate
# du[1] = S; du[2] = I; du[3] = V
ρ = 1 - (u[1] + u[2])/κ # rho: logistic factor
ϡ = (δ*u[1]*u[3]) # upsampi : infected bacteria
du[1] = (μ*u[1]*ρ) - ϡ - (ω*u[1])
du[2] = ϡ - (η*u[2]) - (ω*u[2])
du[3] = (β*η*u[2]) - ϡ - (λ*u[3]) - (ω*u[3])
end
## initial parameters
begin
mu = 0.16 # μ: default growth rate
kappa = 2.2e7 # κ: default carrying capacity
omega = 0.05 # ω: default outflow
eta = 0.025 # η: default τ reciprocal
delta = 1e-9 # δ: default adsorption rate
beta = 50 # β: default burst size
lambda = 0 # λ: default decay rate
s0 = 1e5 # default starting amount of naive bacteria
i0 = 0 # default starting amount of infected bacteria
v0 = 1e5 # default starting amount of phages
tm = 2000.0 # default time span
end
## set ODE
begin
u0 = [s0, i0, v0]
tspan = [0.0, tm]
parms = (μ=mu, κ=kappa, ω=omega, δ=delta, η=eta, β=beta, λ=lambda)
end
## run ODE
begin
prob = ODEProblem(odeSolver!, u0, tspan, parms)
soln = solve(prob, Rosenbrock23())
end
## set obervables
p_mod = Observable(parms)
u_mod = Observable("")
lab_str = Observable("")
## plot
# sliders
S_values = LinRange(0.0, 1.0e7, 100)
I_values = LinRange(0.0, 1.0e7, 100)
V_values = LinRange(0.0, 1.0e7, 100)
mu_values = LinRange(0.0, 1.0, 100)
kappa_values = LinRange(0.0, 1.0e9, 100)
omega_values = LinRange(0.0, 1.0, 100)
# instantiate figure
fig = Figure()
# set axis
ax = Axis(fig[1, 1:2], title = title_str)
# add sliders
Label(fig[2,1], "Growth rate")
mu_sld = Slider(fig[2, 2], range = mu_values, startvalue = mu)
Label(fig[3,1], "Carrying capacity")
k_sld = Slider(fig[3, 2], range = kappa_values, startvalue = kappa)
Label(fig[4,1], "Outflow rate")
o_sld = Slider(fig[4, 2], range = omega_values, startvalue = omega)
# update initial concentration
T = LinRange(tspan[1], tspan[2], 500)
# update parameters
on(mu_sld.value) do val
p_mod.val[1] = val
p_mod[] = p_mod[]
end
on(k_sld.value) do val
p_mod.val[2] = val
p_mod[] = p_mod[]
end
on(o_sld.value) do val
p_mod.val[3] = val
p_mod[] = p_mod[]
end
data = lift(p_mod) do p_new
# this code is called each time the slider value changes, the new value is 'p_val':
title_str[] = "Growth rate = $(round(p_new[1], digits = 2)),
Carrying capacity = $(round(p_new[2], digits = 2)),
Outflow rate = $(round(p_new[3], digits = 2))"
return solve(prob, Rosenbrock23(), p = p_new, saveat = T)
end
# draw
X = @lift $data[1,:]
Y = @lift $data[2,:]
lines!(ax, T, X)
lines!(ax, T, Y)
fig
I also tried with:
julia> data = lift(p_mod) do p_new
new_parms = (μ=p_new, κ=kappa, ω=omega, δ=delta, η=eta, β=beta, λ=lambda)
return solve(prob, Rosenbrock23(), p = new_parms, saveat = T)
end
ERROR: MethodError: no method matching *(::@NamedTuple{μ::Float64, κ::Float64, ω::Float64, δ::Float64, η::Float64, β::Int64, λ::Int64}, ::Float64)
Closest candidates are:
*(::Any, ::Any, ::Any, ::Any...)
@ Base operators.jl:587
*(::Polynomials.AbstractPolynomial, ::Union{Number, Matrix})
@ Polynomials ~/.julia/packages/Polynomials/5ZhzG/src/common.jl:1041
*(::ChainRulesCore.ZeroTangent, ::Any)
@ ChainRulesCore ~/.julia/packages/ChainRulesCore/zgT0R/src/tangent_arithmetic.jl:104
...
Stacktrace:
[1] *
@ ./operators.jl:587 [inlined]
[2] odeSolver!(du::Vector{…}, u::Vector{…}, p::@NamedTuple{…}, t::Float64)
@ Main ~/Documents/Model/MkBact_1.jl:17
[3] (::SciMLBase.Void{typeof(odeSolver!)})(::Vector{Float64}, ::Vararg{Any})
@ SciMLBase ~/.julia/packages/SciMLBase/Dwomw/src/utils.jl:482
[4] (::FunctionWrappers.CallWrapper{…})(f::SciMLBase.Void{…}, arg1::Vector{…}, arg2::Vector{…}, arg3::@NamedTuple{…}, arg4::Float64)
@ FunctionWrappers ~/.julia/packages/FunctionWrappers/Q5cBx/src/FunctionWrappers.jl:65
[5] macro expansion
@ ~/.julia/packages/FunctionWrappers/Q5cBx/src/FunctionWrappers.jl:137 [inlined]
[6] do_ccall
@ ~/.julia/packages/FunctionWrappers/Q5cBx/src/FunctionWrappers.jl:125 [inlined]
[7] FunctionWrapper
@ ~/.julia/packages/FunctionWrappers/Q5cBx/src/FunctionWrappers.jl:144 [inlined]
[8] _call
@ ~/.julia/packages/FunctionWrappersWrappers/9XR0m/src/FunctionWrappersWrappers.jl:12 [inlined]
[9] FunctionWrappersWrapper
@ ~/.julia/packages/FunctionWrappersWrappers/9XR0m/src/FunctionWrappersWrappers.jl:10 [inlined]
[10] ODEFunction
@ ~/.julia/packages/SciMLBase/Dwomw/src/scimlfunctions.jl:2168 [inlined]
[11] initialize!(integrator::OrdinaryDiffEq.ODEIntegrator{…}, cache::OrdinaryDiffEq.Rosenbrock23Cache{…})
@ OrdinaryDiffEq ~/.julia/packages/OrdinaryDiffEq/DmspS/src/perform_step/rosenbrock_perform_step.jl:10
[12] __init(prob::ODEProblem{…}, alg::Rosenbrock23{…}, timeseries_init::Tuple{}, ts_init::Tuple{}, ks_init::Tuple{}, recompile::Type{…}; saveat::LinRange{…}, tstops::Tuple{}, d_discontinuities::Tuple{}, save_idxs::Nothing, save_everystep::Bool, save_on::Bool, save_start::Bool, save_end::Nothing, callback::Nothing, dense::Bool, calck::Bool, dt::Float64, dtmin::Float64, dtmax::Float64, force_dtmin::Bool, adaptive::Bool, gamma::Rational{…}, abstol::Nothing, reltol::Nothing, qmin::Rational{…}, qmax::Int64, qsteady_min::Int64, qsteady_max::Rational{…}, beta1::Nothing, beta2::Nothing, qoldinit::Rational{…}, controller::Nothing, fullnormalize::Bool, failfactor::Int64, maxiters::Int64, internalnorm::typeof(DiffEqBase.ODE_DEFAULT_NORM), internalopnorm::typeof(LinearAlgebra.opnorm), isoutofdomain::typeof(DiffEqBase.ODE_DEFAULT_ISOUTOFDOMAIN), unstable_check::typeof(DiffEqBase.ODE_DEFAULT_UNSTABLE_CHECK), verbose::Bool, timeseries_errors::Bool, dense_errors::Bool, advance_to_tstop::Bool, stop_at_next_tstop::Bool, initialize_save::Bool, progress::Bool, progress_steps::Int64, progress_name::String, progress_message::typeof(DiffEqBase.ODE_DEFAULT_PROG_MESSAGE), progress_id::Symbol, userdata::Nothing, allow_extrapolation::Bool, initialize_integrator::Bool, alias_u0::Bool, alias_du0::Bool, initializealg::OrdinaryDiffEq.DefaultInit, kwargs::@Kwargs{})
@ OrdinaryDiffEq ~/.julia/packages/OrdinaryDiffEq/DmspS/src/solve.jl:518
[13] __init (repeats 5 times)
@ ~/.julia/packages/OrdinaryDiffEq/DmspS/src/solve.jl:11 [inlined]
[14] #__solve#761
@ ~/.julia/packages/OrdinaryDiffEq/DmspS/src/solve.jl:6 [inlined]
[15] __solve
@ ~/.julia/packages/OrdinaryDiffEq/DmspS/src/solve.jl:1 [inlined]
[16] #solve_call#34
@ ~/.julia/packages/DiffEqBase/z0BFe/src/solve.jl:612 [inlined]
[17] solve_up(prob::ODEProblem{…}, sensealg::Nothing, u0::Vector{…}, p::@NamedTuple{…}, args::Rosenbrock23{…}; kwargs::@Kwargs{…})
@ DiffEqBase ~/.julia/packages/DiffEqBase/z0BFe/src/solve.jl:1081
[18] solve_up
@ ~/.julia/packages/DiffEqBase/z0BFe/src/solve.jl:1067 [inlined]
[19] solve(prob::ODEProblem{…}, args::Rosenbrock23{…}; sensealg::Nothing, u0::Nothing, p::@NamedTuple{…}, wrap::Val{…}, kwargs::@Kwargs{…})
@ DiffEqBase ~/.julia/packages/DiffEqBase/z0BFe/src/solve.jl:1004
[20] (::var"#21#22")(p_new::@NamedTuple{μ::Float64, κ::Float64, ω::Float64, δ::Float64, η::Float64, β::Int64, λ::Int64})
@ Main ~/Documents/Model/MkBact_1.jl:123
[21] #map#13
@ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:570 [inlined]
[22] map(::var"#21#22", ::Observable{@NamedTuple{…}})
@ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:568
[23] top-level scope
@ ~/Documents/Model/MkBact_1.jl:120
Some type information was truncated. Use `show(err)` to see complete types.