Lorenz model using OrdinaryDiffEq
Making the app
Here is the model that we'll compile.
using OrdinaryDiffEq
using WebAssemblyCompiler
function lorenz!(du,u,p,t)
σ,ρ,β = p
du[1] = σ*(u[2]-u[1])
du[2] = u[1]*(ρ-u[3]) - u[2]
du[3] = u[1]*u[2] - β*u[3]
end
u0 = [1.0,0.0,0.0]
tspan = (0.0,100.0)
p = (10.0,28.0,8/3)
prob = ODEProblem{true, SciMLBase.FullSpecialize}(lorenz!,u0,tspan,p) # try to avoid FunctionWrappers with FullSpecialize
const integ = init(prob, Tsit5(), dense = true)
Here's a simple solver function. It references integ
, a predefined integrator. WebAssembly stores that as a global variable.
function update()
OrdinaryDiffEq.reinit!(integ)
integ.p = update_params()
sol = solve!(integ)
t = collect(0:0.001:100)
u1 = Float64[sol(tt)[1] for tt in t]
u2 = Float64[sol(tt)[2] for tt in t]
u3 = Float64[sol(tt)[3] for tt in t]
update_output(t, u1, u2, u3)
nothing
end
These utilities update the page inputs.
mdpadnum(x) = @jscall("x => mdpad[x]", Float64, Tuple{Externref}, JS.object(x))
update_params() = (mdpadnum("p1"), mdpadnum("p2"), mdpadnum("p3"))
This function plots the results.
@inline function update_output(t, u1, u2, u3)
xydata = ((x = u1, y = u2, type = "line", name = "x"),)
xylayout = (width = 400.0, height = 400.0, margin = (t = 20., b = 20., l = 20., r = 20.))
config = (responsive = true,)
plotly("xyplot", xydata, xylayout, config)
tdata = ((x = t, y = u1, type = "line", name = "x"),
(x = t, y = u2, type = "line", name = "y"),
(x = t, y = u3, type = "line", name = "z"))
tlayout = (width = 900.0, height = 300.0, margin = (t = 20., b = 20.))
plotly("timeplot", tdata, tlayout, config)
nothing
end
plotly(id, data, layout, config) =
@jscall("(id, data, layout, config) => Plotly.newPlot(id, data, layout, config)",
Nothing, Tuple{Externref, Externref, Externref, Externref},
JS.object(id), data, layout, config)
Before compiling, we need to override some error checks that caused failures.
using Base.Experimental: @overlay
@overlay WebAssemblyCompiler.MT @inline SciMLBase.check_error(integrator::SciMLBase.DEIntegrator) = SciMLBase.ReturnCode.Success
# The following function includes a try/catch, so bypass it.
@overlay WebAssemblyCompiler.MT @inline OrdinaryDiffEq.ode_determine_initdt(args...; kw...) = 0.001
Compile update
to WebAssembly:
compile((update,); filepath = "lorenz/lorenz.wasm", validate = true)
update()
runs automatically whenever inputs are changed.
examples/lorenz/lorenz.jl
also includes some raw HTML to load Plotly and mdpad and to load the WebAssembly file.