Skip to content

Commit f6f76da

Browse files
committed
up
1 parent e6c3857 commit f6f76da

File tree

2 files changed

+23
-57
lines changed

2 files changed

+23
-57
lines changed

docs/pages.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ pages = Any[
5151
"steady_state_functionality/bifurcation_diagrams.md",
5252
"steady_state_functionality/dynamical_systems.md",
5353
"Examples" => Any[
54-
"steady_state_functionality/examples/bifurcationkit_periodic_orbits.md"
54+
"steady_state_functionality/examples/nullcline_plotting.md",
55+
"steady_state_functionality/examples/bifurcationkit_periodic_orbits.md",
5556
"steady_state_functionality/examples/bifurcationkit_codim2.md"
5657
]
5758
],

docs/src/steady_state_functionality/examples/nullcline_plotting.md

Lines changed: 21 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# [Plotting Nullclines and Steady States in Phase Space](@id nullcline_plotting)
2-
In this tutorial we will show how to extract a system's steady states and [nullclines](https://en.wikipedia.org/wiki/Nullcline), and how to plot these in [phase space](https://en.wikipedia.org/wiki/Phase_space). Generally, while nullclines are not directly needed for much analysis, plotting these can give some understanding of a system's steady state and stability properties. While nullclines can be "brute forced" (by solving their equations across grids of values), we will here use [BifurcationKit.jl](https://github.com/bifurcationkit/BifurcationKit.jl) to track their path exactly.
2+
In this tutorial we will show how to extract a system's steady states and [nullclines](https://en.wikipedia.org/wiki/Nullcline), and how to plot these in [phase space](https://en.wikipedia.org/wiki/Phase_space). Generally, while nullclines are not directly needed for much analysis, plotting these can give some understanding of a system's steady state and stability properties.
33

44
For an ordinary differential equation
55
```math
@@ -13,20 +13,14 @@ For an ordinary differential equation
1313
the $i$'th nullcline is the surface along which $\frac{dx_i}{dt} = 0$, i.e. the implicit surface given by $f_i(x_1,\dots,x_n) = 0$. Nullclines are frequently used when visualizing the phase-planes of two-dimensional models (as these can be easily plotted).
1414

1515
## [Computing nullclines and steady states for a bistable switch](@id nullcline_plotting_computation)
16-
For our example we will use a simple bistable switch model, consisting of two species ($X$ and $Y$) which mutually inhibit each other through repressive Hill functions. We will create our model [programmatically](@ref programmatic_CRN_construction).
16+
For our example we will use a simple bistable switch model, consisting of two species ($X$ and $Y$) which mutually inhibit each other through repressive Hill functions.
1717
```@example nullcline_plotting
1818
using Catalyst
19-
t = default_t()
20-
@parameters v K n
21-
@species X(t) Y(t)
22-
rxs = [
23-
Reaction(hillr(Y, v, K, n), [], [X]),
24-
Reaction(hillr(X, v, K, n), [], [Y]),
25-
Reaction(1.0, [X], []),
26-
Reaction(1.0, [Y], []),
27-
]
28-
@named bs_switch = ReactionSystem(rxs, t)
29-
bs_switch = complete(bs_switch)
19+
bs_switch = @reaction_network begin
20+
hillr(Y, v, K, n), 0 --> X
21+
hillr(X, v, K, n), 0 --> Y
22+
1.0, (X,Y) --> 0
23+
end
3024
```
3125

3226
Next, we compute the steady states [using homotopy continuation](@ref homotopy_continuation).
@@ -36,45 +30,23 @@ ps = [v => 1.0, K => 0.6, n => 4.0]
3630
sss = hc_steady_states(bs_switch, ps; show_progress = false)
3731
```
3832

39-
Finally, we will compute the nullclines. We will first extract our model's steady state equations (which we do by creating a `NonlinearSystem`).
40-
```@example nullcline_plotting
41-
nlsys = convert(NonlinearSystem, bs_switch)
42-
eqs = equations(nlsys)
43-
```
44-
Here, each equation is an expression of the two variables ($X$ and $Y$). We wish to plot $Y$ as a function of $X$. To do this, we will substitute the species variable $X$ with the parameter $Xpar$. This will enable us to carry out bifurcation analysis of the equations' solutions as $Xpar$ is varied
45-
```@example nullcline_plotting
46-
@parameters Xpar
47-
nc_eq_X = substitute(equations(nlsys)[2], Dict([X => Xpar]))
48-
nc_eq_Y = substitute(equations(nlsys)[1], Dict([X => Xpar]))
49-
```
50-
To input these into BifurcationKit we need to convert these into `NonlinearSystem`s.
33+
Finally, we will compute the nullclines. First we create a function which, for species values $(X,Y)$, returns the evaluation of the model's ODE's right-hand side.
5134
```@example nullcline_plotting
52-
@named nc_X_sys = NonlinearSystem([nc_eq_X])
53-
@named nc_Y_sys = NonlinearSystem([nc_eq_Y])
54-
nc_X_sys = complete(nc_X_sys)
55-
nc_Y_sys = complete(nc_Y_sys)
56-
nothing # hide
57-
```
58-
Finally, for our nullcline equations, we can use BifurcationKit's `continuation` function to track their solutions across all values of $Xpar$ (the workflow is similar to when we used `bifurcationdiagram` [here](@ref bifurcation_diagrams)).
59-
```@example nullcline_plotting
60-
using BifurcationKit
61-
span = (0.0, 1.2)
62-
function compute_nullcline(nc_sys)
63-
bprob = BifurcationProblem(nc_sys, [Y => 1.0], [ps; Xpar => 0.1], Xpar)
64-
opts_br = ContinuationPar(p_min = span[1], p_max = span[2], dsmax = 0.01)
65-
return continuation(bprob, PALC(), opts_br; bothside = true)
35+
nlprob = NonlinearProblem(complete(nlsys), [X => 0.0, Y => 0.0], ps)
36+
function get_XY(Xval, Yval)
37+
prob = Catalyst.remake(nlprob; u0 = [X => Xval, Y => Yval])
38+
return prob[equations(nlsys)[2].rhs], prob[equations(nlsys)[1].rhs]
6639
end
67-
nc_X = compute_nullcline(nc_X_sys)
68-
nc_Y = compute_nullcline(nc_Y_sys)
69-
nothing # hide
7040
```
71-
72-
We are now 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). For the nullclines, we plot their `.x` values (the variable we solve for, $Y$) against their `.param` values (the parameter which value we vary, $Xpar$).
41+
Next, we plot our nullclines by using Plot.jl's [`contour` function](https://docs.juliaplots.org/latest/series_types/contour/). Here, we plot the $0$ contour lines (which corresponds to the nullclines) for both the $X$ and $Y$ nullclines. 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).
7342
```@example nullcline_plotting
7443
using Plots
75-
# Plot the nullclines.
76-
plot(nc_X.param, nc_X.x; label = "dX/dt = 0", lw = 7, la = 0.7)
77-
plot!(nc_Y.param, nc_Y.x; label = "dY/dt = 0", lw = 7, la = 0.7)
44+
# Plot the nullclines. Line labels added in separate `plot` commands (due to how the `contour` works).
45+
plt_mesh = 0:0.02:1.25
46+
contour(plt_mesh, plt_mesh, (x,y) -> get_XY(x,y)[1]; levels = [0.0], lw = 7, la = 0.7, color = 1, cbar=false)
47+
contour!(plt_mesh, plt_mesh, (x,y) -> get_XY(x,y)[2]; levels = [0.0], lw = 7, la = 0.7, color = 2, cbar=false)
48+
plot!([]; label = "dX/dt = 0", lw = 7, la = 0.7, color = 1)
49+
plot!([]; label = "dY/dt = 0", lw = 7, la = 0.7, color = 2)
7850
7951
# Plot the steady states.
8052
for ss in sss
@@ -93,19 +65,12 @@ plot!(xlimit = span, ylimit = span, xguide = "X", yguide = "Y", legendfontsize =
9365
```
9466
Here we can see how the steady states occur at the nullclines intersections.
9567

96-
!!! warn
97-
BifurcationKit's `continuation` function will not detect disjoint branches, and here we can only use it because we know that each nullcline consists of a single branch. To handle nullclines with disjoint branches, consider using [deflated continuation](@ref bifurcation_diagrams_disjoint_branches) (or possibly a package like [Contour.jl](https://github.com/JuliaGeometry/Contour.jl)).
68+
!!! note
69+
Here we use an inherent Plots function to plot the nullclines. However, there are also specialised packages for these kinds of plots, such as [ImplicitPlots.jl](https://github.com/saschatimme/ImplicitPlots.jl).
9870

9971
## [Plotting system directions in phase space](@id nullcline_plotting_directions)
10072
One useful property of nullclines is that the sign of $dX/dt$ will only switch whenever the solution crosses the $dX/dt=0$ nullcline. This means that, within each region defined by the nullclines, the direction of the solution remains constant. Below we use this to, for each such region, plot arrows showing the solution's direction.
10173
```@example nullcline_plotting
102-
# Create a function which evaluates the ODE at a point in phase space.
103-
nlprob = NonlinearProblem(complete(nlsys), [X => 0.0, Y => 0.0], ps)
104-
function get_XY(Xval, Yval)
105-
prob = Catalyst.remake(nlprob; u0 = [X => Xval, Y => Yval])
106-
return prob[equations(nlsys)[2].rhs], prob[equations(nlsys)[1].rhs]
107-
end
108-
10974
# Creates a function for plotting the ODE's direction at a point in phase space.
11075
function plot_xy_arrow!(Xval, Yval)
11176
dX, dY = get_XY(Xval, Yval)

0 commit comments

Comments
 (0)