Skip to content

Commit cb5bd1e

Browse files
committed
Another pass through the text
1 parent 0d12ce2 commit cb5bd1e

File tree

1 file changed

+32
-23
lines changed

1 file changed

+32
-23
lines changed

docs/src/steady_state_functionality/examples/nullcline_plotting.md

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
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 systems steady state and stability properties. While nullclines can be "brute forced", we will here use [BifurcationKit.jl](https://github.com/bifurcationkit/BifurcationKit.jl) to rack 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. 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.
33

4-
For a ordinary differential equation
4+
For an ordinary differential equation
55
```math
66
\begin{aligned}
7-
\frac{dx_1}{dt} &= f_1(x_1, x_2, ..., x_3) \\
8-
\frac{dx_2}{dt} &= f_2(x_1, x_2, ..., x_3) \\
7+
\frac{dx_1}{dt} &= f_1(x_1, x_2, ..., x_n) \\
8+
\frac{dx_2}{dt} &= f_2(x_1, x_2, ..., x_n) \\
99
&\vdots\\
10-
\frac{dx_n}{dt} &= f_3(x_1, x_2, ..., x_3) \\
10+
\frac{dx_n}{dt} &= f_n(x_1, x_2, ..., x_n) \\
1111
\end{aligned}
1212
```
13-
The nullclines are the curves
13+
the nullclines are the curves
1414
```math
1515
\begin{aligned}
16-
0 &= f_1(x_1, x_2, ..., x_3) \\
17-
0 &= f_2(x_1, x_2, ..., x_3) \\
16+
0 &= f_1(x_1, x_2, ..., x_n) \\
17+
0 &= f_2(x_1, x_2, ..., x_n) \\
1818
&\vdots\\
19-
0 &= f_3(x_1, x_2, ..., x_3) \\
19+
0 &= f_n(x_1, x_2, ..., x_n) \\
2020
\end{aligned}
2121
```
22-
where the $i$'th nullclines is the curve along which $\frac{dx_i}{dt} = 0$. Generally, nullclines are primarily computed for models with 2 variable (as only here can they be easily plotted).
22+
where the $i$'th nullclines is the curve along which $\frac{dx_i}{dt} = 0$. Generally, nullclines are primarily computed for models with 2 variables (as these can be easily plotted).
2323

24-
## [Computing nucclines and steady states for a bistable switch](@id nullcline_plotting_computation)
25-
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).
24+
## [Computing nullclines and steady states for a bistable switch](@id nullcline_plotting_computation)
25+
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).
2626
```@example nullcline_plotting
2727
using Catalyst
2828
t = default_t()
@@ -50,11 +50,11 @@ Finally, we will compute the nullclines. We will first extract our model's stead
5050
nlsys = convert(NonlinearSystem, bs_switch)
5151
eqs = equations(nlsys)
5252
```
53-
Here, each equation is an expression of 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
53+
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
5454
```@example nullcline_plotting
5555
@parameters Xpar
56-
nc_eq_X = substitute(equations(nlsys)[1], Dict([X => Xpar]))
57-
nc_eq_Y = substitute(equations(nlsys)[2], Dict([X => Xpar]))
56+
nc_eq_X = substitute(equations(nlsys)[2], Dict([X => Xpar]))
57+
nc_eq_Y = substitute(equations(nlsys)[1], Dict([X => Xpar]))
5858
```
5959
To input these into BifurcationKit we need to convert these into `NonlinearSystem`s.
6060
```@example nullcline_plotting
@@ -64,7 +64,7 @@ nc_X_sys = complete(nc_X_sys)
6464
nc_Y_sys = complete(nc_Y_sys)
6565
nothing # hide
6666
```
67-
Finally, for out 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)).
67+
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)).
6868
```@example nullcline_plotting
6969
using BifurcationKit
7070
span = (0.0, 1.2)
@@ -78,11 +78,14 @@ nc_Y = compute_nullcline(nc_Y_sys)
7878
nothing # hide
7979
```
8080

81-
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).
81+
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$).
8282
```@example nullcline_plotting
8383
using Plots
84-
plot(nc_X.x, nc_X.param; label = "dX/dt = 0", lw = 7, la = 0.7)
85-
plot!(nc_Y.x, nc_Y.param; label = "dY/dt = 0", lw = 7, la = 0.7)
84+
# Plot the nullclines.
85+
plot(nc_X.param, nc_X.x; label = "dX/dt = 0", lw = 7, la = 0.7)
86+
plot!(nc_Y.param, nc_Y.x; label = "dY/dt = 0", lw = 7, la = 0.7)
87+
88+
# Plot the steady states.
8689
for ss in sss
8790
color, markershape = if steady_state_stability(ss, bs_switch, ps)
8891
:blue, :circle
@@ -93,29 +96,35 @@ for ss in sss
9396
end
9497
scatter!([], []; color = :blue, markershape = :circle, label = "Stable stead state")
9598
scatter!([], []; color = :red, markershape = :star4, label = "Unstable stead state")
96-
plot!(xlimit = span, ylimit = span, xguide = "X", yguide = "Y", legendfontsize = 10, size = (500,500))
99+
100+
# Finishing touches.
101+
plot!(xlimit = span, ylimit = span, xguide = "X", yguide = "Y", legendfontsize = 10, size = (600,600))
97102
```
98103
Here we can see how the steady states occur at the nullclines intersections.
99104

100105
!!! warn
101-
BifurcationKit's `continuation` function will not detect disjoint branches, and above we can only use it because we know that each nullcline consists of a single branch. To handle disjoint nullclines, consider using [deflated continuation](@ref bifurcation_diagrams_disjoint_branches) (or possibly a package like [Contour.jl](https://github.com/JuliaGeometry/Contour.jl)).
106+
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)).
102107

103108
## [Plotting system directions in phase space](@id nullcline_plotting_directions)
104-
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 mean 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.
105-
109+
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.
106110
```@example nullcline_plotting
111+
# Create a function which evaluates the ODE at a point in phase space.
107112
nlprob = NonlinearProblem(complete(nlsys), [X => 0.0, Y => 0.0], ps)
108113
function get_XY(Xval, Yval)
109114
prob = Catalyst.remake(nlprob; u0 = [X => Xval, Y => Yval])
110115
return prob[equations(nlsys)[2].rhs], prob[equations(nlsys)[1].rhs]
111116
end
117+
118+
# Creates a function for plotting the ODE's direction at a point in phase space.
112119
function plot_xy_arrow!(Xval, Yval)
113120
dX, dY = get_XY(Xval, Yval)
114121
dX = dX > 0 ? 0.05 : -0.05
115122
dY = dY > 0 ? 0.05 : -0.05
116123
plot!([Xval, Xval + dX], [Yval, Yval]; color = :black, lw = 2, arrow = :arrow, label = "")
117124
plot!([Xval, Xval], [Yval, Yval + dY]; color = :black, lw = 2, arrow = :arrow, label = "")
118125
end
126+
127+
# Plots the ODE's direction in phase space at selected positions.
119128
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)]
120129
foreach(pos -> plot_xy_arrow!(pos...), arrow_positions)
121130
plot!()

0 commit comments

Comments
 (0)