Output Functions
Mix.install([
{:integrator, github: "woodward/integrator"},
{:kino_vega_lite, "~> 0.1"}
])
Usage
An output function lets you plot the results of an integration or simulation while it’s occurring, or send the data to an animation tool. Let’s see how that works.
First, we’ll need to set up an empty chart to receive the data:
alias VegaLite, as: VL
alias Integrator.SampleEqns
alias Integrator.Point
chart =
VL.new(
width: 600,
height: 400,
title: "Solution of van der Pol Equation (μ = 1) with Dormand-Prince45"
)
|> VL.mark(:line, point: true, tooltip: true)
|> VL.encode_field(:x, "t", type: :quantitative)
|> VL.encode_field(:y, "x", type: :quantitative)
|> VL.encode_field(:color, "x_value", type: :nominal)
|> Kino.VegaLite.new()
|> Kino.render()
Now, let’s connect an output function to the simulation, and we’ll inject a Process.sleep(50)
on purpose so that the simulation takes a while. Watch the chart above while you run the simulation below.
t_initial = Nx.f64(0.0)
t_final = Nx.f64(20.0)
x_initial = Nx.f64([2.0, 0.0])
output_fn = fn points ->
points = if is_list(points), do: points, else: [points]
points = points |> Enum.map(&Point.to_number(&1))
points
|> Enum.map(fn point ->
%Point{t: t, x: x} = point
data =
[
%{t: t, x: List.first(x), x_value: "x[0]"},
%{t: t, x: List.last(x), x_value: "x[1]"}
]
data |> Enum.each(& Kino.VegaLite.push(chart, &1))
end)
end
opts = [output_fn: output_fn, type: :f64, speed: 1.0]
solution =
Integrator.integrate(&SampleEqns.van_der_pol_fn/2, t_initial, t_final, x_initial, opts)