Running a model in CytoZoo.jl#
CytoZoo.jl is a Julia package of cardiac cell
models that share one interface: a model is a callable struct with the
DifferentialEquations.jl signature f(du, u, p, t), which knows its states, parameters and
monitored values by name. gotranx ode2cytozoo generates such a model from an ODE file, so
the model can be solved with any DifferentialEquations.jl solver, and coupled to other
CytoZoo models.
In this example we generate the O’Hara-Rudy model with the Land model of contraction
(ORdmm_Land.ode), solve one beat, and see what blocking half of the L-type calcium current
does to the action potential and the active tension.
This page is a Julia script, examples/cytozoo/main.jl. To run it yourself you need the
gotranx command on your PATH, and the packages in examples/Project.toml:
julia --project=examples -e 'using Pkg; Pkg.instantiate()'
cd examples/cytozoo && julia --project=.. main.jl
First we generate the model. --model-name names its struct
run(`gotranx ode2cytozoo ORdmm_Land.ode -o ordmm_land.jl --model-name ORdmmLand`);
The generated file defines names such as rhs and NUM_STATES at its top level, so we
include it in a module of its own
using CytoZoo
module Generated
include("ordmm_land.jl")
end;
Constructing the struct gives the model with the parameters and initial values of the ODE file
model = Generated.ORdmmLand()
num_states(model), num_parameters(model)
(48, 139)
The model is the right-hand side, and CytoZoo builds an ODEProblem from it. The ODE file
applies a stimulus during the first half millisecond, so a second of simulation is one beat.
Cardiac cell models are stiff, so we use an implicit solver
using OrdinaryDiffEqBDF
problem = ODEProblem(model, (0.0, 1000.0))
control = solve(problem, FBDF(); abstol = 1e-8, reltol = 1e-8)
control.retcode
ReturnCode.Success = 1
Parameters live in a vector on the struct, and parameter_index finds one by name. Here we
halve the L-type calcium current
blocked = Generated.ORdmmLand()
blocked.parameters[parameter_index(blocked, :scale_drug_ICaL)] = 0.5
drug = solve(ODEProblem(blocked, (0.0, 1000.0)), FBDF(); abstol = 1e-8, reltol = 1e-8)
drug.retcode
ReturnCode.Success = 1
The membrane potential is a state, and the active tension Ta is one of the monitored
values, which monitor_history recomputes along a solution
using Plots
v = transmembrane_potential_index(model)
function tension(solution, cell)
history = monitor_history(solution, cell)
return history.t, history.values[findfirst(==(:Ta), history.names), :]
end
potential = plot(xlabel = "Time (ms)", ylabel = "v (mV)")
active = plot(xlabel = "Time (ms)", ylabel = "Ta (kPa)")
for (solution, cell, label) in ((control, model, "control"), (drug, blocked, "ICaL × 0.5"))
plot!(potential, solution.t, [u[v] for u in solution.u]; label)
plot!(active, tension(solution, cell)...; label)
end
plot(potential, active; layout = (2, 1), size = (700, 600))
With less calcium entering the cell, the action potential is shorter, and the peak active tension is about 40 % lower.