Anshul Singhvi / May 15 2019
Remix of Julia by Nextjournal
Interactive Differential Equations with Makie.jl
First, the setup (installing the requisite packages and all):
# setup using Pkg pkg"add DifferentialEquations ParameterizedFunctions Observables Makie"
1. Defining the ODE
This is a bog-standard use of DifferentialEquations.jl
- you could substitute your own differential equation here with ease.
using DifferentialEquations, ParameterizedFunctions lorenz = Lorenz begin # define the system dx = σ * (y - x) dy = x * (ρ - z) - y dz = x * y - β*z end σ ρ β u0 = [1.0,0.0,0.0] # initial conditions tspan0 = (0.0,100.0) # initial timespan p0 = [10.0,28.0,8/3] # initial parameters prob = ODEProblem(lorenz, u0, tspan0, p0) # define the problem
[36mODEProblem[0m with uType [36mArray{Float64,1}[0m and tType [36mFloat64[0m. In-place: [36mtrue[0m
timespan: (0.0, 100.0)
u0: [1.0, 0.0, 0.0]
2. Plotting and Interactivity
2.1. Setting up sliders
One of the easiest ways to get interactivity in Makie.jl
is the slider. In this case, I've used textslider
, because it automatically vbox
es a title with the slider, and it returns both the slider plot object and the Observable corresponding to its value.
using Makie using AbstractPlotting: textslider OME = 8 # the order of magnitude to range between sσ, oσ = textslider(exp10.(-OME:0.001:OME), "σ", start = p0[1]); sρ, oρ = textslider(exp10.(-OME:0.001:OME), "ρ", start = p0[2]); sβ, oβ = textslider(exp10.(-OME:0.001:OME), "β", start = p0[3]); st, ot = textslider(exp10.(-OME:0.001:OME), "tₘₐₓ", start = tspan0[end]); sr, or = textslider(100:10000, "resolution", start = 2000);
2.2. Setting up Observables
Now comes the real magic! With Observables
, it's easy to map the user input to some plottable data.
trange = lift(ot, or) do tmax, resolution LinRange(0.0, tmax, resolution) end data = lift(oσ, oρ, oβ, trange) do σ, ρ, β, ts Point3f0.( solve( remake( prob; p = [σ, ρ, β], tspan = (ts[1], ts[end]) ) )(ts).u )# change to fit the dimensionality - maybe even return 2 arrays, # or `Point2`s... end
Observable{Array{Point{3,Float32},1}} with 0 listeners. Value:
Point{3,Float32}[[1.0, 0.0, 0.0], [0.870411, 1.2043, 0.0255712], [1.27426, 2.5588, 0.114683], [2.2047, 4.73013, 0.392357], [3.93103, 8.47733, 1.27947], [6.95172, 14.6847, 4.0488], [11.7644, 23.1173, 11.9061], [17.5358, 27.6391, 28.6857], [19.7077, 16.5379, 46.0587], [14.4848, -1.19503, 46.4486] … [-3.84543, -1.40298, 25.6916], [-2.99238, -1.96104, 22.742], [-2.75226, -2.77679, 20.2129], [-2.98538, -3.87808, 18.1292], [-3.64744, -5.41572, 16.58], [-4.78281, -7.59138, 15.7858], [-6.49026, -10.5425, 16.2019], [-8.82477, -14.0388, 18.6174], [-11.5318, -16.8149, 23.779], [-13.6645, -16.3805, 30.9178]]
2.3. Plotting the data
We now have plottable data! It's a simple matter to plot the data and the slider together (we'll use a line plot for the data):
three = lines(data) vbox(hbox(sσ, sρ, sβ, st, sr), three)