Hello,
I am trying to run a simulation of a bacterial population with Julia. I set a function that takes one parameter (growth rate, mu) that should change at a specific time point (t_in). I set the CallbackSet for the DifferentialEquations package, but I don’t think the condition is implemented because nothing happens to the simulation – the growth does not show any difference. This might be due to the data themselves, but I think I got the syntax wrong.
The CallbackSet was designed to change the amounts of the bacteria, thus, the u parameter. Now I need to change p parameter and I am not sure if I got it right. I am pasting a snippet of the code for syntax checking. Is it correct?
Thank you
function dual_logist!(du, u, p, t)
# parameters
μ = p[:μ] # bacterial growth rate
...
# ODE
du[1] = (μ[1]*u[1]*ł) - (δ[1]*u[1]*u[5]) - (ω*u[1])
du[2] = (μ[2]*u[2]*ł) - (δ[2]*u[2]*u[6]) - (ω*u[2])
...
end
using DifferentialEquations
mu = [mu_0, nu_0]
u0 = ...
tspan = (0, 500)
t_in = 100
...
condition_1(u, t, integrator) = t_in # time
affect_1!(integrator) = μ[1] = mu_1 # amount
cb1 = ContinuousCallback(condition_1, affect_1!)
condition_2(u, t, integrator) = t_in # time
affect_2!(integrator) = μ[2] = nu_1 # amount
cb2 = ContinuousCallback(condition_2, affect_2!)
parms = (μ=mu...)
conditions = CallbackSet(cb1, cb2)
prob = ODEProblem(dual_logist!, u0, tspan, parms)
soln = solve(prob, Rosenbrock23(), callback=conditions, tstops=[t_in])
Thank you, I tried with:
condition_1(u, t, integrator) = t_in
affect_1!(integrator.p) = μ[1] = mu_1
cb1 = ContinuousCallback(condition_1, affect_1!)
...
soln = solve(prob, Rosenbrock23(), callback=conditions)
but I got the error:
syntax: "integrator.p" is not a valid function argument
So I tried with:
affect_1!(p) = μ[1] = mu_1 # amount
and it worked. But I still do not see any difference. To check if a modification is introduced, I set mu_1 to zero. Is the syntax correct?
Oops, sorry, I mean:
affect_1!(integrator) = integrator.p[:μ] = mu_1
(If the p
is mutable)
or
affect_1!(integrator) = integrator.p = a_new_p_with_mu_1_updated
(If the p
is immutable)
I’m not sure about the structure of p
. Could it be a NamedTuple
or a LabelledArray
?
Thank you, it is a named tuple:
julia> typeof(parms)
NamedTuple{(:μ, :κ, :ω, :δ, :η, :β, :λ), Tuple{Vector{Float64}, Float64, Int64, Vararg{Vector{Float64}, 4}}}
How to create a new NamedTuple based on the old one
nt = (a=1, b=2, c=3)
newnt = merge(nt, (; b=10)) #(a=1,b=10,c=3)
EDIT: I noticed p.μ
is an array, so this might work
function affect_1!(integrator)
integrator.p.μ[1] = mu_1
end
Or, if you don’t want side effects of mutating the array:
function affect_1!(integrator)
# Copy the array and assign a new value at the 1st element
newμ = replace(integrator.p.μ, 1 => mu_1)
# Replace the old params with the new ones
integrator.p = merge(integrator.p, (; μ=newμ ))
end
Since your parameter set has two tiers, I would like to recommend the package ComponentArrays.jl, which handles nested named components better.
I tried with this approach, but again there was no difference in the data…
Thanks to a tip from Steffen P. it turned out the problem was in the time condition: it should be
condition_1(u, t, integrator) = t - t_in
Now it works as you indicated.
Thank you for the support.