Skip to content

Commit 96ecf22

Browse files
committed
init
1 parent 40953ac commit 96ecf22

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed

docs/pages.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ pages = Any[
4242
"steady_state_functionality/nonlinear_solve.md",
4343
# "steady_state_functionality/steady_state_stability_computation.md",
4444
"steady_state_functionality/bifurcation_diagrams.md",
45-
"steady_state_functionality/dynamical_systems.md"
45+
"steady_state_functionality/dynamical_systems.md",
46+
"Examples" => Any[
47+
"steady_state_functionality/examples/nullcline_plotting.md"
48+
]
4649
],
4750
"Inverse problems" => Any[
4851
"inverse_problems/petab_ode_param_fitting.md",
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# [Plotting Nullclines and Steady States in Phase Space](@id nullcline_plotting)
2+
3+
Introduction text.
4+
5+
For our example we will use a simple bistable switch model, consisting of two species ($X$ and $Y$) which mutually inhibits each other through repressive hill functions. We will create our model [programmatically](@ref programmatic_CRN_construction).
6+
```@example nullcline_plotting
7+
using Catalyst
8+
t = default_t()
9+
@parameters v K n
10+
@species X(t) Y(t)
11+
rxs = [
12+
Reaction(hillr(Y, v, K, n), [], [X]),
13+
Reaction(hillr(X, v, K, n), [], [Y]),
14+
Reaction(1.0, [X], []),
15+
Reaction(1.0, [Y], []),
16+
]
17+
@named bs_switch = ReactionSystem(rxs, t)
18+
bs_switch = complete(bs_switch)
19+
```
20+
Next, we compute the steady states [using homotopy continuation](@ref homotopy_continuation).
21+
```@example nullcline_plotting
22+
import HomotopyContinuation
23+
ps = [v => 1.0, K => 0.6, n => 4.0]
24+
sss = hc_steady_states(bs_switch, ps; show_progress = false)
25+
```
26+
Next we wish to compute the nullclines. We will first extract the equations corresponding to these from our model. Next, we will compute them using [BifurcationKit.jl](https://github.com/bifurcationkit/BifurcationKit.jl). To generate our equations, we first convert our model to a `NonlinearSystem`. For each nullcline equation, we also have to replace the corresponding variable with a parameter (which can be varied to compute the full nullcline curve).
27+
```@example nullcline_plotting
28+
@parameters Xval Yval
29+
nlsys = convert(NonlinearSystem, bs_switch)
30+
nc_eq_X = substitute(equations(nlsys)[1], Dict([X => Xval]))
31+
nc_eq_Y = substitute(equations(nlsys)[2], Dict([Y => Yval]))
32+
```
33+
Next, we compute a `NonlinerSystem` corresponding to each nullcline.
34+
```@example nullcline_plotting
35+
@named nc_X_sys = NonlinearSystem([nc_eq_X])
36+
@named nc_Y_sys = NonlinearSystem([nc_eq_Y])
37+
nc_X_sys = complete(nc_X_sys)
38+
nc_Y_sys = complete(nc_Y_sys)
39+
```
40+
Finally, for each of these, we can use BifurcationKit's `continuation` function to track the solution of the nullclines as the corresponding variable (the workflow is similar to when we used `bifurcationdiagram` [here](@ref bifurcation_diagrams)).
41+
```@example nullcline_plotting
42+
using BifurcationKit
43+
span = (0.0, 1.2)
44+
function compute_nullcline(nc_sys, span, var_solve, pvar_bif)
45+
bprob = BifurcationProblem(nc_sys, [var_solve => 1.0], [ps; pvar_bif => 1.0], pvar_bif; plot_var = var_solve)
46+
opts_br = ContinuationPar(p_min = span[1], p_max = span[2], dsmax = 0.01) # `dsmax = 0.01` ensures a smooth plot.
47+
return continuation(bprob, PALC(), opts_br; bothside = true)
48+
end
49+
nc_X = compute_nullcline(nc_X_sys, span, Y, Xval)
50+
nc_Y = compute_nullcline(nc_Y_sys, span, X, Yval)
51+
nothing # hide
52+
```
53+
54+
Finally, we are ready to create our plot. We will plot the steady states using `scatter`. We use the `steady_state_stability` function to [compute steady state stabilities](@ref steady_state_stability) (and use this to determine how to plot the steady state markers).
55+
```@example nullcline_plotting
56+
using Plots
57+
plot(nc_X; vars = (:x, :param), label = "dX/dt = 0", lw = 7, la = 0.7)
58+
plot!(nc_Y; vars = (:param, :x), label = "dY/dt = 0", lw = 7, la = 0.7)
59+
for ss in sss
60+
color, markershape = if steady_state_stability(ss, bs_switch, ps)
61+
:blue, :circle
62+
else
63+
:red, :star4
64+
end
65+
scatter!((ss[2], ss[1]); color, markershape, label = "", markersize = 10)
66+
end
67+
scatter!([], []; color = :blue, markershape = :circle, label = "Stable stead state")
68+
scatter!([], []; color = :red, markershape = :star4, label = "Unstable stead state")
69+
plot!(xlimit = span, ylimit = span, xguide = "X", yguide = "Y", legendfontsize = 10, size = (500,500))
70+
```
71+
Here we can see how the steady states occur at the nullclines intersections.
72+
73+
## [Plotting system directions in phase space](@id nullcline_plotting_directions)
74+
75+
```@example nullcline_plotting
76+
nlprob = NonlinearProblem(complete(nlsys), [X => 0.0, Y => 0.0], ps)
77+
function get_XY(Xval, Yval)
78+
prob = Catalyst.remake(nlprob; u0 = [X => Xval, Y => Yval])
79+
return prob[equations(nlsys)[2].rhs], prob[equations(nlsys)[1].rhs]
80+
end
81+
function plot_xy_arrow!(Xval, Yval)
82+
dX, dY = get_XY(Xval, Yval)
83+
dX = dX > 0 ? 0.05 : -0.05
84+
dY = dY > 0 ? 0.05 : -0.05
85+
plot!([Xval, Xval + dX], [Yval, Yval]; color = :black, lw = 2, arrow = :arrow, label = "")
86+
plot!([Xval, Xval], [Yval, Yval + dY]; color = :black, lw = 2, arrow = :arrow, label = "")
87+
end
88+
arrow_positions = [(0.25, 0.25), (0.75, 0.75), (0.35, 0.8), (0.8, 0.35), (0.02, 1.1), (1.1, 0.02)]
89+
foreach(pos -> plot_xy_arrow!(pos...), arrow_positions)
90+
plot!()
91+
```

0 commit comments

Comments
 (0)