Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

using Startup
using OrdinaryDiffEq
using SteadyStateDiffEq
using DiffEqCallbacks
using Catalyst
using Plots
Plots.gr(linewidth=1.5)
Plots.GRBackend()

Convenience functions to plot gradients of the ODEs.

function meshgrid(xrange, yrange)
    xx = [x for y in yrange, x in xrange]
    yy = [y for y in yrange, x in xrange]
    return xx, yy
end

"Get the gradients of the vector field of an ODE system at a grid of points in the state space."
function get_gradient(prob, xsym, ysym, xx, yy; t = nothing)
    # The order of state variables (unknowns) in the ODE system is not guaranteed. So we may need to swap the order of x and y when calling ∂F.
    swap_or_not(x, y; xidx=1) = xidx == 1 ? [x, y] : [y, x]
    ∂F = prob.f
    ps = prob.p
    sys = prob.f.sys
    xidx = ModelingToolkit.variable_index(sys, xsym)
    yidx = ModelingToolkit.variable_index(sys, ysym)
    dx = map((x, y) -> ∂F(swap_or_not(x, y; xidx), ps, t)[xidx], xx, yy)
    dy = map((x, y) -> ∂F(swap_or_not(x, y; xidx), ps, t)[yidx], xx, yy)
    return (dx, dy)
end

function normalize_gradient(dx, dy, xscale, yscale; transform=cbrt)
    maxdx = maximum(abs, dx)
    maxdy = maximum(abs, dy)
    dx_norm = @. transform(dx / maxdx) * xscale
    dy_norm = @. transform(dy / maxdy) * yscale
    return (dx_norm, dy_norm)
end
normalize_gradient (generic function with 1 method)

Fig 7.7

Model of lac operon in E. coli.

@time "Build system" rn707 = @reaction_network begin
    (a1 / (1 + RToverK1 * (K2 / (K2 + L))^4), δM), 0 <--> M
    (c1 * M, δY), 0 <--> Y
    mm(Le, kL * Y, KML), 0 --> L
    δL, L --> 0
    (Y/4) * mm(L, 2kg, KMg), L => 0
end
Build system: 0.006885 seconds (2.93 k allocations: 5.127 MiB, 76.69% compilation time)
Loading...
up707 = Dict(
    :δM => 0.48, :δY => 0.03, :δL => 0.02,
    :a1 => 0.29,:K2 => 2.92e6, :RToverK1 => 213.2, :c1 => 18.8,
    :kL => 6e4, :KML => 680.0, :kg => 3.6e3, :KMg => 7e5,
    :Le => 0.0, :M => 0.01, :Y => 0.1, :L => 0.0,
)

@unpack Le = rn707
cbs = let
    event_le1 = PresetTimeCallback([500.0], (integrator) -> integrator.ps[Le] = 50)
    event_le2 = PresetTimeCallback([1000.0], (integrator) -> integrator.ps[Le] = 100)
    event_le3 = PresetTimeCallback([1500.0], (integrator) -> integrator.ps[Le] = 150)
    event_le4 = PresetTimeCallback([2000.0], (integrator) -> integrator.ps[Le] = 0)
    CallbackSet(event_le1, event_le2, event_le3, event_le4)
end
SciMLBase.CallbackSet{Tuple{}, Tuple{SciMLBase.DiscreteCallback{DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#14#18"}, Main.var"##332".var"#14#18", DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#14#18"}, typeof(SciMLBase.FINALIZE_DEFAULT), Nothing, Tuple{}}, SciMLBase.DiscreteCallback{DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#15#19"}, Main.var"##332".var"#15#19", DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#15#19"}, typeof(SciMLBase.FINALIZE_DEFAULT), Nothing, Tuple{}}, SciMLBase.DiscreteCallback{DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#16#20"}, Main.var"##332".var"#16#20", DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#16#20"}, typeof(SciMLBase.FINALIZE_DEFAULT), Nothing, Tuple{}}, SciMLBase.DiscreteCallback{DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#17#21"}, Main.var"##332".var"#17#21", DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#17#21"}, typeof(SciMLBase.FINALIZE_DEFAULT), Nothing, Tuple{}}}}((), (SciMLBase.DiscreteCallback{DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#14#18"}, Main.var"##332".var"#14#18", DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#14#18"}, typeof(SciMLBase.FINALIZE_DEFAULT), Nothing, Tuple{}}(DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#14#18"}([500.0], true, SciMLBase.INITIALIZE_DEFAULT, Main.var"##332".var"#14#18"()), Main.var"##332".var"#14#18"(), DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#14#18"}([500.0], true, SciMLBase.INITIALIZE_DEFAULT, Main.var"##332".var"#14#18"()), SciMLBase.FINALIZE_DEFAULT, Bool[1, 1], nothing, (), true), SciMLBase.DiscreteCallback{DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#15#19"}, Main.var"##332".var"#15#19", DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#15#19"}, typeof(SciMLBase.FINALIZE_DEFAULT), Nothing, Tuple{}}(DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#15#19"}([1000.0], true, SciMLBase.INITIALIZE_DEFAULT, Main.var"##332".var"#15#19"()), Main.var"##332".var"#15#19"(), DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#15#19"}([1000.0], true, SciMLBase.INITIALIZE_DEFAULT, Main.var"##332".var"#15#19"()), SciMLBase.FINALIZE_DEFAULT, Bool[1, 1], nothing, (), true), SciMLBase.DiscreteCallback{DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#16#20"}, Main.var"##332".var"#16#20", DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#16#20"}, typeof(SciMLBase.FINALIZE_DEFAULT), Nothing, Tuple{}}(DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#16#20"}([1500.0], true, SciMLBase.INITIALIZE_DEFAULT, Main.var"##332".var"#16#20"()), Main.var"##332".var"#16#20"(), DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#16#20"}([1500.0], true, SciMLBase.INITIALIZE_DEFAULT, Main.var"##332".var"#16#20"()), SciMLBase.FINALIZE_DEFAULT, Bool[1, 1], nothing, (), true), SciMLBase.DiscreteCallback{DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#17#21"}, Main.var"##332".var"#17#21", DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#17#21"}, typeof(SciMLBase.FINALIZE_DEFAULT), Nothing, Tuple{}}(DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#17#21"}([2000.0], true, SciMLBase.INITIALIZE_DEFAULT, Main.var"##332".var"#17#21"()), Main.var"##332".var"#17#21"(), DiffEqCallbacks.PresetTimeFunction{Vector{Float64}, typeof(SciMLBase.INITIALIZE_DEFAULT), Main.var"##332".var"#17#21"}([2000.0], true, SciMLBase.INITIALIZE_DEFAULT, Main.var"##332".var"#17#21"()), SciMLBase.FINALIZE_DEFAULT, Bool[1, 1], nothing, (), true)))

Fig 7.07 (A)

tend = 2500.0
@time "Build problem" prob707a = ODEProblem(rn707, up707, tend)
@time "Solve problem" sol = solve(prob707a, KenCarp47(), callback=cbs)

plot(sol, idxs=[:Y], labels="β-galactosidase monomer", title="Fig 7.07 (A)", xlabel="Time (min)", ylabel="Concentration")
plot!(t -> 50 * (500<=t<1000) + 100 * (1000<=t<1500) + 150 * (1500<=t<2000), 0, tend, linestyle=:dash, label="External lactose")
Build problem: 0.741959 seconds (1.13 M allocations: 86.183 MiB, 95.52% compilation time)
Solve problem: 3.608248 seconds (3.96 M allocations: 269.031 MiB, 1.99% gc time, 99.87% compilation time)
Plot{Plots.GRBackend() n=2}

Fig 7.07 (B)

Comparing the original model and the modified model

@time "Build system" rn707b = @reaction_network begin
    (a1 / (1 + RToverK1 * (K2 / (K2 + L))^4), δM), 0 <--> M
    (c1 * M, δY), 0 <--> Y
    Enz * mm(Le, 4kL, KML), 0 --> L
    δL, L --> 0
    Enz * mm(L, 2kg, KMg), L => 0
end
Build system: 0.001282 seconds (2.63 k allocations: 118.102 KiB)
Loading...
up707b = merge(up707, Dict(:Enz => 40.0))
prob707a = SteadyStateProblem(rn707, up707)
prob707b = SteadyStateProblem(rn707b, up707b)
lerange = 0:100
prob_func = (prob, i, repeat) -> remake(prob, p=[:Le => lerange[i]])
output_func=(sol, i) -> (sol[:Y] / 4, false)
alg = DynamicSS(KenCarp47())

eprob = EnsembleProblem(prob707a; prob_func, output_func)
eprob_mod = EnsembleProblem(prob707b; prob_func, output_func)
@time sim = solve(eprob, alg; trajectories=length(lerange))
@time sim_mod = solve(eprob_mod, alg; trajectories=length(lerange))

plot(lerange, [sim.u sim_mod.u], labels=["Original model" "Modified model"], title="Fig 7.07 (B)", xlabel="External lactose concentration", ylabel="β-galactosidase tetramer concentration")
  5.341346 seconds (9.21 M allocations: 643.359 MiB, 2.77% gc time, 165.17% compilation time)
  5.675767 seconds (9.23 M allocations: 645.362 MiB, 2.41% gc time, 154.63% compilation time)
Plot{Plots.GRBackend() n=2}

Fig 7.11

Model of phage lambda decision switch

using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using OrdinaryDiffEq
using Plots
using DisplayAs: PNG
function build_sys711(; name=:model711, simplify=true)
    @parameters K1=1 K2=0.1 K3=5 K4=0.5 delta_r=0.02 delta_c=0.02 a=5 b=50
    @variables r(t)=0 c(t)=0 rd(t) cd(t)
    rd = r / 2
    cd = c / 2
    f1 = K1 * rd^2
    f2 = K2 * rd
    f3 = K3 * cd
    f4 = K4 * cd
    den = 1 + f1 * (1 + f2) + f3 * (1 + f4)
    Dr = a * (1 + 10 * f1) / den - delta_r * r
    Dc = b * (1 + f3) / den - delta_c * c
    eqs = [
        D(r) ~ Dr,
        D(c) ~ Dc
    ]
    sys = ODESystem(eqs, t; name)
    return simplify ? mtkcompile(sys) : sys
end
build_sys711 (generic function with 1 method)
@time "Build system" sys711 = build_sys711()
@time "Build problem" prob711 = ODEProblem(sys711, [], (0.0, 6000.0))
Build system: 0.026938 seconds (13.12 k allocations: 770.172 KiB, 80.71% compilation time)
Build problem: 0.701581 seconds (1.10 M allocations: 81.290 MiB, 96.13% compilation time)
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true Initialization status: FULLY_DETERMINED Non-trivial mass matrix: false timespan: (0.0, 6000.0) u0: 2-element Vector{Float64}: 0.0 0.0
xrange = range(0, 250, 101)
yrange = range(0, 250, 101)

@unpack r, c, delta_r = sys711
xx, yy = meshgrid(xrange, yrange)
dx, dy = get_gradient(prob711, r, c, xx, yy)
xx_sp = xx[1:5:end, 1:5:end]
yy_sp = yy[1:5:end, 1:5:end]
dxnorm, dynorm = normalize_gradient(dx[1:5:end, 1:5:end], dy[1:5:end, 1:5:end], 5*step(xrange), 5*step(yrange))
([6.869900720736457 12.5 … -3.830155728956546 -4.380542181421769; 1.3564603768501013 8.619503079010805 … -3.8342111861666335 -4.383088669865122; … ; 0.2083496781398712 -2.33161695290965 … -4.605918652952202 -4.917933080036901; 0.20140128284987327 -2.352372751490533 … -4.662569067589072 -4.9609030705045996], [12.5 3.117281732278567 … 0.2207518698458821 0.20998650981913586; 7.803052086269383 6.81094309069183 … -2.111870814296849 -2.1154713520348527; … ; -5.350151664320803 -5.3508185574376315 … -5.648346145868846 -5.654984302895317; -5.479329459661966 -5.4798755632023 … -5.746740666493403 -5.75327189248827])
quiver(xx_sp, yy_sp, quiver=(dxnorm, dynorm); aspect_ratio=1, size=(600, 600), xlims=(0, 250), ylims=(0, 250), color=:gray)
contour!(xrange, yrange, dx, levels=[0], line=(:black, :solid), colorbar=false)
plot!([], [],  line=(:black, :solid), label="R nullcline")
contour!(xrange, yrange, dy, levels=[0],  line=(:black, :dash), colorbar=false)
plot!([], [], line=(:black, :dash), label="C nullcline")
plot!(xlabel="[cI] (nM)", ylabel="[cro] (nM)", title="Fig. 7.11 (A)", legend=:topright)
Plot{Plots.GRBackend() n=25}

Fig 7.11 (B)

prob711b = remake(prob711, p=[delta_r => 0.2])
dx, dy = get_gradient(prob711b, r, c, xx, yy)
dxnorm, dynorm = normalize_gradient(dx[1:5:end, 1:5:end], dy[1:5:end, 1:5:end], 5*step(xrange), 5*step(yrange))
quiver(xx_sp, yy_sp, quiver=(dxnorm, dynorm); aspect_ratio=1, size=(600, 600), xlims=(0, 250), ylims=(0, 250), color=:gray)
contour!(xrange, yrange, dx, levels=[0], line=(:black, :solid), colorbar=false)
plot!([], [],  line=(:black, :solid), label="R nullcline")
contour!(xrange, yrange, dy, levels=[0],  line=(:black, :dash), colorbar=false)
plot!([], [], line=(:black, :dash), label="C nullcline")
plot!(xlabel="[cI] (nM)", ylabel="[cro] (nM)", title="Fig. 7.11 (B)", legend=:topright)
Plot{Plots.GRBackend() n=25}

Fig 7.17

Goodwin oscillator model: Goodwin model (biology)

using OrdinaryDiffEq
using Catalyst
using Plots

@time "Build system" rn717 = @reaction_network begin
    (a / (k^n + Z^n), b), 0 <--> X
    (α * X, β), 0 <--> Y
    (γ * Y, δ), 0 <--> Z
end
Build system: 0.000914 seconds (2.02 k allocations: 90.672 KiB)
Loading...
up717 = Dict(:a => 360.0, :k => 1.368, :b => 1.0, :α => 1.0, :β => 0.6, :γ => 1.0, :δ => 0.8, :n => 12.0, :X => 0.0, :Y => 0.0, :Z => 0.0)

tend = 35.0
@time "Build problem" prob717 = ODEProblem(rn717, up717, (0.0, tend))
@time "Solve problem" sol717 = solve(prob717, TRBDF2())
plot(sol717, idxs=[:X, :Y, :Z], labels=["X" "Y" "Z"], title="Fig 7.17 (A)", xlabel="Time", ylabel="Concentration")
Build problem: 0.656201 seconds (1.05 M allocations: 78.010 MiB, 96.00% compilation time)
Solve problem: 0.632174 seconds (910.48 k allocations: 64.308 MiB, 99.40% compilation time)
Plot{Plots.GRBackend() n=3}
plot(sol717, idxs=(:X, :Y, :Z), labels=false, title="Fig 7.17 (B)", size=(600, 600), xlabel="X", ylabel="Y", zlabel="Z")
Plot{Plots.GRBackend() n=1}

Fig 7.19

Circadian rhythm model

using OrdinaryDiffEq
using Catalyst
using Plots
Plots.gr(linewidth=1.5)

@time "Build system" rn719 = @reaction_network begin
    hillr(PN, vs, ki, n), 0 --> M
    mm(M, vm, km1), M => 0
    ks * M, 0 --> P0
    mm(P0, v1, k1), P0 => P1
    mm(P1, v2, k2), P1 => P0
    mm(P1, v3, k3), P1 => P2
    mm(P2, v4, k4), P2 => P1
    k1, P2 --> PN
    k2, PN --> P2
    mm(P2, vd, kd), P2 => 0
end
Build system: 0.053276 seconds (189.64 k allocations: 13.277 MiB, 97.62% compilation time)
Loading...
up719 = Dict(
    :vs => 0.76, :vm => 0.65, :vd => 0.95, :ks => 0.38, # :kt1 => 1.9, # :kt2 => 1.3,
    :v1 => 3.2, :v2 => 1.58, :v3 => 5.0, :v4 => 2.5,
    :k1 => 1.0, :k2 => 1.0, :k3 => 2.0, :k4 => 2.0, :ki => 1.0, :km1 => 0.5, :kd => 0.2, :n => 4,
    :M => 1.0, :P0 => 1.0, :P1 => 0.0, :P2 => 0.0, :PN => 0.0
)

tspan = (-50.0, 200.0)
@time "Build problem" prob719 = ODEProblem(rn719, up719, tspan)
@time "Solve problem" sol719 = solve(prob719, TRBDF2())
@unpack M, P0, P1, P2, PN = rn719
totalP = P0 + P1 + P2 + PN
plot(sol719, idxs=[M, PN, totalP], labels=["M" "Nuclear PER" "Total PER"], xlabel="Time", ylabel="Concentration", title="Fig 7.19 (A)")
Build problem: 1.299624 seconds (2.08 M allocations: 147.498 MiB, 3.70% gc time, 97.04% compilation time)
Solve problem: 0.643092 seconds (967.21 k allocations: 67.776 MiB, 99.34% compilation time)
Plot{Plots.GRBackend() n=3}

Fig 7.21

Repressilator model.

using OrdinaryDiffEq
using Catalyst
using Plots
Plots.gr(linewidth=1.5)

@time "Build system" rn721 = @reaction_network begin
    (α0 + hillr(C, α, 1, n), 1), 0 <--> mA
    (α0 + hillr(A, α, 1, n), 1), 0 <--> mB
    (α0 + hillr(B, α, 1, n), 1), 0 <--> mC
    (β * mA, β), 0 <--> A
    (β * mB, β), 0 <--> B
    (β * mC, β), 0 <--> C
end
Build system: 0.074030 seconds (108.55 k allocations: 7.625 MiB, 97.86% compilation time)
Loading...
up721 = Dict(
    :α => 298.2, :α0 => 0.03, :n => 2.0, :β => 0.2,
    :mA => 0.2, :mB => 0.3, :mC => 0.4,
    :A => 0.1, :B => 0.1, :C => 0.5
)

tend = 300.0
@time "Build problem" prob721 = ODEProblem(rn721, up721, (0.0, tend))
@time "Solve problem" sol721 = solve(prob721, TRBDF2())

plot(sol721, idxs=[:A, :B, :C], xlabel="Time", ylabel="Concentration", title="Fig 7.21", legend=:left)
Build problem: 0.725435 seconds (1.03 M allocations: 76.139 MiB, 5.54% gc time, 95.87% compilation time)
Solve problem: 0.648992 seconds (940.19 k allocations: 66.227 MiB, 99.13% compilation time)
Plot{Plots.GRBackend() n=3}

Fig 7.23

Hasty synthetic oscillator model

v0_723(x, y, alpha, sigma) = (1 + x^2 + alpha * sigma * x^4) / ((1 + x^2 + sigma * x^4) * (1 + y^4))
@time "Build system" rn723 = @reaction_network begin
    v0_723(x, y, alpha, sigma), 0 --> x
    ay * v0_723(x, y, alpha, sigma), 0 --> y
    gammax, x --> 0
    gammay, y --> 0
end
Build system: 0.033480 seconds (123.63 k allocations: 8.886 MiB, 96.48% compilation time)
Loading...
up723 = Dict(
    :alpha => 11.0, :sigma => 2.0, :gammax => 0.2, :gammay => 0.012, :ay => 0.2,
    :x => 0.3963, :y => 2.3346,
)
tend = 300.0
@time "Build problem" prob723 = ODEProblem(rn723, up723, (0.0, tend))
@time "Solve problem" sol723 = solve(prob723, TRBDF2())
plot(sol723, idxs=[:x, :y], xlabel="Time", ylabel="Concentration", title="Fig 7.23 (A)", legend=:right)
Build problem: 0.668371 seconds (1.01 M allocations: 75.044 MiB, 95.07% compilation time)
Solve problem: 0.652781 seconds (943.98 k allocations: 66.302 MiB, 99.41% compilation time)
Plot{Plots.GRBackend() n=2}

Vector field

xrange = range(0, 1.5, 51)
yrange = range(0, 3, 51)
@unpack x, y = rn723

xx, yy = meshgrid(xrange, yrange)
dx, dy = get_gradient(prob723, x, y, xx, yy)
contour(xrange, yrange, dx, levels=[0], line=(:black, :solid), colorbar=false)
plot!([], [], line=(:black, :solid), label="x nullcline")
contour!(xrange, yrange, dy, levels=[0], line=(:black, :dash), colorbar=false)
plot!([], [], line=(:black, :dash), label="y nullcline")
plot!(sol723, idxs=(x, y), label="Trajectory")
plot!(xlabel="x", ylabel="y", title="Fig 7.23 (B)", legend=:bottomright, size=(600, 600))
Plot{Plots.GRBackend() n=5}

Fig 7.25

model of quorum sensing mechanism of Vibrio fischeri

using OrdinaryDiffEq
using SteadyStateDiffEq
using Catalyst
using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using Plots

Model with/without feedback

function build_sys725(; feedback=true, simplify=true, name=:model725)
    hil(x, k) = x / (k + x)
    hil(x, k, n) = hil(x^n, k^n)
    @parameters k0 = 0.0008 k1 = 0.5 k2 = 0.02 n = 0.6 a = 10 b = 0.07 a0 = 0.05 KM = 0.01 RT = 0.5 diff = 1000 popsize = 1000
    @variables A(t) = 0 I(t) = 0 Rstar(t) = 0 Aout(t) = 0 R0(t)
    R0 = RT - 2Rstar
    v0 = feedback ? k0 * I : k0 * 15.0
    v1 = k1 * A^2 * R0^2
    v2 = k2 * Rstar
    va = n * (A - Aout)
    eqs = [
        R0 ~ RT - 2Rstar,
        D(A) ~ v0 - 2v1 + 2v2 - va,
        D(I) ~ a0 + a * hil(Rstar, KM) - b * I,
        D(Rstar) ~ v1 - v2,
        D(Aout) ~ popsize * va - diff * Aout
    ]
    sys = ODESystem(eqs, t; name)
    return simplify ? mtkcompile(sys) : sys
end
build_sys725 (generic function with 1 method)
@time "Build system" sys725 = build_sys725()
@time "Build system" sys725_no_feedback = build_sys725(feedback=false)
@time "Build problem" prob725 = SteadyStateProblem(sys725, [])
@time "Build problem" prob725_no_feedback = SteadyStateProblem(sys725_no_feedback, [])
Build system: 0.022104 seconds (20.56 k allocations: 1.156 MiB, 73.91% compilation time)
Build system: 0.014296 seconds (12.47 k allocations: 624.320 KiB, 73.86% compilation time)
Build problem: 0.945606 seconds (1.35 M allocations: 100.358 MiB, 4.32% gc time, 97.04% compilation time)
Build problem: 0.328540 seconds (683.92 k allocations: 49.230 MiB, 96.72% compilation time)
SteadyStateProblem with uType Vector{Float64}. In-place: true u0: 4-element Vector{Float64}: 0.0 0.0 0.0 0.0
npops = 1:50:5001
trajectories = length(npops)
alg = DynamicSS(KenCarp47())
@unpack popsize, I = sys725
prob_func = (prob, i, repeat) -> remake(prob, p=[popsize => npops[i]])
eprob = EnsembleProblem(prob725; prob_func)
eprob_nofeed = EnsembleProblem(prob725_no_feedback; prob_func)
@time sim = solve(eprob, alg; trajectories);
@time sim_no_feed = solve(eprob_nofeed, alg; trajectories);
  7.597975 seconds (10.36 M allocations: 715.407 MiB, 2.26% gc time, 161.26% compilation time)
  8.519320 seconds (9.46 M allocations: 653.668 MiB, 1.17% gc time, 125.30% compilation time)
luxI = map(s -> s[I], sim)
luxI_nofeed = map(s -> s[I], sim_no_feed)
plot(npops, [luxI, luxI_nofeed], xscale=:log10, xlabel="Population size", ylabel="LuxI concentration (μM)", title="Fig. 7.25", label=["With feedback" "Without feedback"], legend=:topleft)
Plot{Plots.GRBackend() n=2}

Fig 7.28

model of synthetic pulse generating system

using OrdinaryDiffEq
using Catalyst
using Plots

function vg728(R, C, KR, KC, aG)
    fR = R / KR
    fC = C / KC
    return aG * (fR / (1 + fR + fC^2 + fR * fC^2))
end

@time "Build system" rn728 = @reaction_network begin
    vg728(R, C, KR, KC, aG), 0 --> G
    bG, G --> 0
    mm(R, aC, KR), 0 --> C
    bC, C --> 0
    k1 * (RT - 2R)^2 * A^2, 0 --> R
    k2, R --> 0
end
Build system: 0.039549 seconds (23.36 k allocations: 1.670 MiB, 95.97% compilation time)
Loading...
up728 = Dict(
    :aG => 80.0, # muM/min
    :bG => 0.07, # /min
    :KC => 0.008, # muM
    :aC => 0.5, # muM/min
    :bC => 0.3, # /min
    :k1 => 0.5, # /muM^3 /min
    :k2 => 0.02, # /min
    :KR => 0.02, # muM
    :RT => 0.5, # muM
    :A => 10.0, # muM
    :G => 0.0,
    :C => 0.0,
    :R => 0.0,
)

tend = 50.0
@time "Build problem" prob728 = ODEProblem(rn728, up728, (0.0, tend))
@time "Solve problem" sol728 = solve(prob728, TRBDF2())

plot(sol728, idxs=[:G, :C, :R], xlabel="Time (min)", ylabel="Concentration (μM)", title="Fig 7.28", label=["GFP" "cI" "LuxR:AHL complex"])
Build problem: 0.699872 seconds (1.07 M allocations: 79.341 MiB, 94.53% compilation time)
Solve problem: 0.824339 seconds (953.93 k allocations: 66.893 MiB, 20.87% gc time, 99.59% compilation time)
Plot{Plots.GRBackend() n=3}

Fig. 7.30

model of synthetic band detector system

using OrdinaryDiffEq
using SteadyStateDiffEq
using Catalyst
using Plots
Plots.gr(linewidth=1.5)

@time "Build system" rn730 = @reaction_network begin
    hillr(L, aG, KL, 2), 0 --> G
    bG, G --> 0
    hillr(C, aL1, KC, 2), 0 --> L
    mm(R, aL2, KR), 0 --> L
    bL, L --> 0
    mm(R, aC, KR), 0 --> C
    bC, C --> 0
    k1 * (RT - 2R)^2 * A^2, 0 --> R
    k2, R --> 0
end
Build system: 0.023529 seconds (87.95 k allocations: 6.144 MiB, 94.56% compilation time)
Loading...
up730 = Dict(
    :aG => 2.0, # muM/min
    :bG => 0.07, # /min
    :KL => 0.8, # muM
    :aL1 => 1, # muM/min
    :aL2 => 1, # muM/min
    :KC => 0.008, # muM
    :bL => 0.02, # /min
    :aC => 1, # muM/min
    :bC => 0.07, # /min
    :k1 => 0.5, # /muM^3 /min
    :k2 => 0.02, # /min
    :KR => 0.01, # muM
    :RT => 0.5, # muM
    :A => 0.1,
    :G => 0.0,
    :L => 0.0,
    :C => 0.0,
    :R => 0.0,
)

(sols, as) = let N = 1:100
    as = [exp10(-4 + 4 * (i - 1) / length(N)) for i in N]
    @time "Build problem" prob = SteadyStateProblem(rn730, up730)
    prob_func = (prob, i, repeat) -> remake(prob, p=[:A => as[i]])
    eprob = EnsembleProblem(prob; prob_func)
    @time "Solve problem" sols = solve(eprob, DynamicSS(TRBDF2()); trajectories=length(N))
    (sols, as)
end;
Build problem: 1.705554 seconds (2.05 M allocations: 147.132 MiB, 36.67% gc time, 98.21% compilation time)
Solve problem: 7.592255 seconds (9.74 M allocations: 677.968 MiB, 8.80% gc time, 155.50% compilation time)
luxI = map(s -> s[:L], sols)
CI = map(s -> s[:C], sols)
GFP = map(s -> s[:G], sols)
plot(as, [luxI, CI, GFP], xscale=:log10, xlabel="AHL concentration (μM)", ylabel="Concentration (μM)", title="Fig. 7.30", label=["LuxI" "cI" "GFP"])
Plot{Plots.GRBackend() n=3}

Fig. 7.31

Hasty synthetic oscillator model

using OrdinaryDiffEq
using Catalyst
using Plots
Plots.gr(linewidth=1.5)

v0_731(x, y, alpha, sigma) = (1 + x^2 + alpha * sigma * x^4) / ((1 + x^2 + sigma * x^4) * (1 + y^4))
@time "Build system" rn731 = @reaction_network begin
    v0_731(x1, y1, alpha, sigma), 0 --> x1
    ay * v0_731(x1, y1, alpha, sigma), 0 --> y1
    v0_731(x2, y2, alpha, sigma), 0 --> x2
    ay * v0_731(x2, y2, alpha, sigma), 0 --> y2
    gammax, x1 --> 0
    gammay, y1 --> 0
    gammax, x2 --> 0
    gammay, y2 --> 0
    (D, D), x1 <--> x2
    (D, D), y1 <--> y2
end
Build system: 0.010345 seconds (8.35 k allocations: 421.422 KiB, 83.88% compilation time)
Loading...
up731 = Dict(
    :alpha => 11.0,
    :sigma => 2.0,
    :gammax => 0.2,
    :gammay => 0.012,
    :ay => 0.2,
    :D => 0.015,
    :x1 => 0.3963,
    :y1 => 2.3346,
    :x2 => 0.5578,
    :y2 => 1.9317,
)

tend = 500.0
@time "Build problem" prob731 = ODEProblem(rn731, up731, (0.0, tend))
@time "Solve problem" sol731 = solve(prob731, TRBDF2())
plot(sol731, xlabel="Time (a.u.)", ylabel="Concentration (a.u.)", title="Fig. 7.31", legend=:left)
Build problem: 0.782510 seconds (1.03 M allocations: 75.983 MiB, 14.86% gc time, 95.42% compilation time)
Solve problem: 0.686594 seconds (1.03 M allocations: 71.651 MiB, 99.40% compilation time)
Plot{Plots.GRBackend() n=4}

Fig. 7.38

stochastic implementation (Gillespie SSA) of a collection of spontaneously decaying molecules.

using JumpProcesses
using Catalyst
using Plots

@time "Build system" rn738 = @reaction_network begin
    d, A --> 0
end
Build system: 0.029728 seconds (20.32 k allocations: 1.371 MiB, 97.75% compilation time)
Loading...
tend = 5.0
ps738 = Dict(:d => 1.0)
@time "Build problem" jprob1000 = JumpProblem(rn738, [:A => 1000], (0.0, tend), ps738)
jprob100 = remake(jprob1000, u0=[:A => 100])
jprob10 = remake(jprob1000, u0=[:A => 10])

@time "Solve problem" sol1000 = solve(jprob1000)
@time "Solve problem" sol100 = solve(jprob100)
@time "Solve problem" sol10 = solve(jprob10)
Build problem: 5.253761 seconds (8.04 M allocations: 575.275 MiB, 12.75% gc time, 99.70% compilation time)
Solve problem: 0.241852 seconds (456.74 k allocations: 31.232 MiB, 99.94% compilation time)
Solve problem: 0.000035 seconds (114 allocations: 14.906 KiB)
Solve problem: 0.000024 seconds (22 allocations: 3.406 KiB)
retcode: Success Interpolation: Piecewise constant interpolation t: 12-element Vector{Float64}: 0.0 0.012623856284435691 0.04977351811263866 0.2703855331207103 0.320746035237543 0.6720705202600807 1.0121040798450294 1.2938934263613973 1.3884338162620515 2.5430653700767074 3.7040710171981885 5.0 u: 12-element Vector{Vector{Int64}}: [10] [9] [8] [7] [6] [5] [4] [3] [2] [1] [0] [0]
plot(sol1000, xlabel="Time", ylabel="# of molecules", title="Fig. 7.38 (A)", label="1000 molecules")
plot!(t -> 1000 * exp(-t), linestyle=:dash, label="ODE solution")
Plot{Plots.GRBackend() n=2}
plot(sol100, xlabel="Time", ylabel="# of molecules", title="Fig. 7.38 (B)", label="100 molecules")
plot!(t -> 100 * exp(-t), linestyle=:dash, label="ODE solution")
Plot{Plots.GRBackend() n=2}
plot(sol10, xlabel="Time", ylabel="# of molecules", title="Fig. 7.38 (C)", label="10 molecules")
plot!(t -> 10 * exp(-t), linestyle=:dash, label="ODE solution")
Plot{Plots.GRBackend() n=2}

This notebook was generated using Literate.jl.