Lorenz attractor with makie

I’m trying to reproduce the Lorenz attractor simulation from here:

https://lazarusa.github.io/gnuplot-examples/menu3/#animations (scroll to the third example)

and I feel like this should be easier or at least there should exist a better solution. In Gnuplot.jl we have the option of plot every #number of frames, so that things could be faster and smooth, I would like to see also that here in Makie. so far, this what I have, (How to get rid off most Nodes definitions and do everything in one call? )

using GLMakie, DifferentialEquations, ParameterizedFunctions
GLMakie.activate!()
let
  g = @ode_def begin
    dx = σ*(y - x)
    dy = x*(ρ - z) - y
    dz = x*y - β*z
  end σ ρ β
  u0 = [1.0; 0.0; 0.0]
  tspan = (0.0, 30.0)
  p = [10.0,28.0,8/3]
  prob = ODEProblem(g, u0, tspan, p)
  sol = solve(prob, Tsit5(), saveat = 0.01)
  x, y, z = sol[1,:], sol[2,:], sol[3,:]
  tempo = sol.t
  # leading point
  xnodePt = Node([x[2]])
  ynodePt = Node([y[2]])
  znodePt = Node([z[2]])

  # line
  xnode = Node(x[1:2])
  ynode = Node(y[1:2])
  znode = Node(z[1:2])
  tnode = Node(tempo[1:2])
  # first frame (figure)
  fig = Figure(resolution=(1000,600), fontsize = 20)
  ax = Axis3(fig, aspect = (1,1,0.5), azimuth = -0.3π, elevation = π/9)
  pltobj = lines!(ax, xnode, ynode, znode, color = tnode, overdraw = false,
    colormap = :plasma)
  scatter!(ax, xnodePt, ynodePt, znodePt, markersize = 15, color = :red)
  cbar = Colorbar(fig, pltobj, label = "time", width = 15, ticksize=15,
                  tickalign = 1, height = Relative(0.5))
  fig[1,1] = ax
  fig[1,2] = cbar
  # the animation is done by updating the nodes values
  record(fig,joinpath(@__DIR__, "output", "animlorenzAttractor.mp4"), framerate = 24*8) do io
      for i in 3:length(tempo)
          push!(xnode[], x[i])
          push!(ynode[], y[i])
          push!(znode[], z[i])
          tnode[] = tempo[1:i]
          xnodePt[] = [x[i]]
          ynodePt[] = [y[i]]
          znodePt[] = [z[i]]
          xnode[] = xnode[] # trigger all updates for the new frame, again?
          autolimits!(ax)
          recordframe!(io)  # record a new frame
      end
    end
end

Bit like pseudocode:

all_points = Point3f0.(sol)
i = Node(1)
points = @lift(view(all_points, 1:$i)))
scatterlines!(points)

record(...) do io
    i[] += 1
    recordframe!(io)
end

The view is pretty neat :slight_smile:
I guess as an alternative:

fig = Figure(resolution=(1000,600), fontsize = 20)
ax = Axis3(fig[1,1], aspect = (1,1,0.5), azimuth = -0.3π, elevation = π/9)
points = Observable(Point3f0[])
color = Observable(Float64[])
attr = (color=color, transparency=true, colormap=:plasma)
pltobj = lines!(ax, points; attr...)
scatter!(ax, points; markersize = 0.02, markerspace=SceneSpace, attr...)
cbar = Colorbar(fig[1,2], pltobj, label = "time", width = 15, ticksize=15, tickalign = 1, height = Relative(0.5))
# the animation is done by updating the nodes values
path = joinpath(@__DIR__, "animlorenzAttractor.mp4")
record(fig, path, enumerate(tempo), framerate = 24*8) do (i, t)
    xyz = Point3f0(sol[:, i])
    push!(points[], xyz)
    push!(color[], t)
    notify(points)
    notify(color)
    autolimits!(ax)
end

In Gnuplot.jl we have the option of plot every #number of frames

You can just loop over the animation however you like, no?

2 Likes

BTW I have implemented trajectory animators in InteractiveDynamics.jl. This:

using InteractiveDynamics, DynamicalSystems, GLMakie

using OrdinaryDiffEq: Tsit5
ds = Systems.lorenz()

u0 = [10,10,10.0]
u0s =  [u0 .+ i*1e-3 for i in 1:3]

diffeq = (alg = Tsit5(), dtmax = 0.01)

figure, obs = interactive_evolution_timeseries(
    ds, u0s; tail = 1000, diffeq, colors = to_color.(COLORSCHEME[1:3]),
    linekwargs = (linewidth = 2.0,)
)

will produce this: https://juliadynamics.github.io/InteractiveDynamics.jl/dev/trajectory/#With-timeseries-1

(can’t paste mp4 in discourse, but the animation is in the link)

The source code of the interactive_evolution is more convoluted than what you want though, because it is set up to run indefinitely.

3 Likes

Indeed, you loop over the animation. Also, I did not use CairoMakie because the lines overlap in the wrong order.