Adding nullclines to a streamplot

Hello there,

I’m still extremely new to Julia and I’m currently trying to plot both the flow of an autonomous system of linear ODEs + their corresponding nullclines. The flow itself I’m using -

using Makie
using CairoMakie; cm = CairoMakie

odeSol(x,y) = Point(y - x^2 + 2, x*y - y^2) #x(t)' = ..., y(t)' = ...
scene = cm.Scene(size =(500,500))
cm.streamplot!(scene, odeSol, -5..5, -5..5,
            backgroundcolor = :black, colormap = cm.Reverse(:plasma),
    gridsize= (30,30), arrow_size = 0.1)

save("odeField.png", scene)

Is there some sophisticated way to add the nullclines here that I should be using?

Thank you in advance a lot.

1 Like

Something like this should do the trick:

using CairoMakie
fig = Figure()
fx(x,y) = y - x^2 + 2
fy(x,y) = x*y - y^2
odeSol(x,y) = Point2(fx(x,y), fy(x,y)) #x(t)' = ..., y(t)' = ...
ax = Makie.Axis(fig[1,1])
streamplot!(ax, odeSol, -5..5, -5..5,
    backgroundcolor = :black, colormap = cm.Reverse(:plasma),
    gridsize= (30,30), arrow_size = 0.1)

xs = range(-5, 5; length = 201)
ys = range(-5, 5; length = 201)

FX = [fx(x,y) for x ∈ xs, y ∈ ys]
FY = [fy(x,y) for x ∈ xs, y ∈ ys]

contour!(ax, xs, ys, FX; levels = [0.0], color = :black)
contour!(ax, xs, ys, FY; levels = [0.0], color = :gray, linestyle = :dash)
fig 

save("odeField.png", fig)

odeField

2 Likes

Thank you a lot! I’ve added some minor aesthetic changes as well

using CairoMakie; cm = CairoMakie
using LaTeXStrings

fig = cm.Figure()
fx(x,y) = y - x^2 + 2
fy(x,y) = x*y - y^2
odeSol(x,y) = Point2(fx(x,y), fy(x,y)) #x(t)' = ..., y(t)' = ...
ax = Makie.Axis(fig[1,1],
                xlabel = "X",
                ylabel = "Y",
                title = "Nullclines and ODE flow field.")
cm.streamplot!(ax, odeSol, -5..5, -5..5,
    colormap = :grays, alpha = 0.2,
    gridsize= (30,30), arrow_size = 0.1)

xs = range(-5, 5; length = 201)
ys = range(-5, 5; length = 201)

FX = [fx(x,y) for x ∈ xs, y ∈ ys]
FY = [fy(x,y) for x ∈ xs, y ∈ ys]

cm.contour!(ax, xs, ys, FX; levels = [0.0], color = :blue, linestyle = :dash,
            linewidth = 2, label = L"\frac{dx}{dt} = 0")
cm.contour!(ax, xs, ys, FY; levels = [0.0], color = :orange, linestyle = :dash,
            linewidth = 2, label = L"\frac{dy}{dt} = 0")
cm.axislegend(ax)
fig

save("odeField.png", fig)

Would you know how to fix the legend so that it displays the colours assigned to the contour lines?

I think for contourlines you have to manually specify the legend entries (not, sure, but the below code does work)

using CairoMakie; cm = CairoMakie
using LaTeXStrings

fig = cm.Figure()
fx(x,y) = y - x^2 + 2
fy(x,y) = x*y - y^2
odeSol(x,y) = Point2(fx(x,y), fy(x,y)) #x(t)' = ..., y(t)' = ...
ax = Makie.Axis(fig[1,1],
                xlabel = "X",
                ylabel = "Y",
                title = "Nullclines and ODE flow field.")
cm.streamplot!(ax, odeSol, -5..5, -5..5,
    colormap = :grays, alpha = 0.2,
    gridsize= (30,30), arrow_size = 0.1)

xs = range(-5, 5; length = 201)
ys = range(-5, 5; length = 201)

FX = [fx(x,y) for x ∈ xs, y ∈ ys]
FY = [fy(x,y) for x ∈ xs, y ∈ ys]

cm.contour!(ax, xs, ys, FX; levels = [0.0], color = :blue, linestyle = :dash,
            linewidth = 2, label = L"\frac{dx}{dt} = 0")
cm.contour!(ax, xs, ys, FY; levels = [0.0], color = :orange, linestyle = :dash,
            linewidth = 2, label = L"\frac{dy}{dt} = 0")

leg_elems = [
    LineElement(color = :blue, linestyle = :dash),
    LineElement(color = :orange, linestyle = :dash)
]
leg_strs = [
    L"\frac{dx}{dt} = 0",
    L"\frac{dy}{dt} = 0"
]

Legend(fig[1,1], leg_elems, leg_strs;
    halign = :right,
    valign = :top,
    tellwidth = false,
    tellheight = false
)
fig
1 Like

Thanks a lot, this is perfect.