NOTE
- This package is now being registered in General.
- This package is tested only with ODEs.
Hi, you all!
I’m glad to share a package SimulationLogger.jl.
This package provides convenient logging macros compatible with DifferentialEquations.jl. Thanks to @jonniedie as the basic form of this package is based on SimulationLogs.jl.
Although this package is not yet mature, from several discussions such as the original question and the idea of this package, I found that this functionality would also be convenient for other users.
The following examples and instruction are borrowed from the original README.md.
Have a nice day!
TL; DR: example code
Example 1: typical usage (see FlightSims.jl for details)
using FlightSims
const FS = FlightSims
using DifferentialEquations
using LinearAlgebra
using Plots
function test()
# linear system
A = [0 1;
0 0]
B = [0;
1]
n, m = 2, 1
env = LinearSystemEnv(A, B) # exported from FlightSims
x0 = State(env)([1.0, 2.0])
p0 = zero.(x0) # auxiliary parameter
# optimal control
Q = Matrix(I, n, n)
R = Matrix(I, m, m)
lqr = LQR(A, B, Q, R) # exported from FlightSims
u_lqr = FS.OptimalController(lqr) # (x, p, t) -> -K*x; minimise J = ∫ (x' Q x + u' R u) from 0 to ∞
# simulation
tf = 10.0
Δt = 0.01
affect!(integrator) = integrator.p = copy(integrator.u) # auxiliary callback
cb = PeriodicCallback(affect!, Δt; initial_affect=true)
@Loggable function dynamics!(dx, x, p, t; u)
@onlylog p # activate this line only when logging data
@log state = x
@log input = u
# nested logging
@nested_log :linear state_square = x .^ 2 # to put additional data into the same symbol (:linear)
@nested_log :linear Dynamics!(env)(dx, x, p, t; u=u)
end
prob, df = sim(
x0, # initial condition
# apply_inputs(Dynamics!(env); u=u_lqr), # dynamics with input of LQR
apply_inputs(dynamics!; u=u_lqr), # dynamics with input of LQR
p0;
tf=tf, # final time
callback=cb,
savestep=Δt,
)
p_x = plot(df.time, hcat(df.state...)';
title="state variable", label=["x1" "x2"], color=[:black :black], lw=1.5,
) # Plots
plot!(p_x, df.time, hcat(df.p...)';
ls=:dash, label="param", color=[:red :orange], lw=1.5
)
savefig("figures/x_lqr.png")
plot(df.time, hcat(df.input...)'; title="control input", label="u") # Plots
savefig("figures/u_lqr.png")
df
end
Example 2: low-level usage
using SimulationLogger
using DifferentialEquations
using Transducers
using Plots
function test()
@Loggable function dynamics!(dx, x, p, t)
@log x
@log u = -x
@onlylog state = x
@onlylog input = u
dx .= u
end
t0, tf = 0.0, 10.0
Δt = 0.01
log_func(x, t, integrator::DiffEqBase.DEIntegrator; kwargs...) = dynamics!(zero.(x), copy(x), integrator.p, t, __LOG_INDICATOR__(); kwargs...)
saved_values = SavedValues(Float64, Dict)
cb = SavingCallback(log_func, saved_values;
saveat=t0:Δt:tf)
# # sim
x0 = [1, 2, 3]
tspan = (t0, tf)
prob = ODEProblem(
dynamics!, x0, tspan;
callback=cb,
)
_ = solve(prob)
ts = saved_values.t
xs = saved_values.saveval |> Map(datum -> datum[:state]) |> collect
us = saved_values.saveval |> Map(datum -> datum[:input]) |> collect
p_x = plot(ts, hcat(xs...)')
p_u = plot(ts, hcat(us...)')
dir_log = "figures"
mkpath(dir_log)
savefig(p_x, joinpath(dir_log, "state.png"))
end
Main macros
@Loggable
@Loggable
is a macro that makes an ODE function loggable.
Example
@Loggable function dynamics!(dx, x, p, t)
dx .= -x
end
Mechanism
@Loggable
generates additional method for the generic function of the annotated function definition.
The additional method receives __log__indicator__::__LOG_INDICATOR__
as the last argument (other arguments are the same as the original function definition).
Notice
- This macro should be used in front of “function definition”. For example,
@Loggable function dynamics!(dx, x, p, t)
dx .= -x
end
is good.
@Loggable dynamics! = (dx, x, p, t) -> dx .= -x
may not work properly.
- Functions annotated by
@Loggable
MUST NOT havereturn
keyword. For example,
@Loggable function dynamics!(dx, x, p, t)
dx .= -x
nothing
end
works fine, but the logging functionality with return
, for example,
@Loggable function dynamics!(dx, x, p, t)
dx .= -x
return nothing
end
may behave poorly.
@log
This macro logs the annotated variable, and also executes the followed expression when both solving DEProblem and logging data.
Example
@Loggable function dynamics!(dx, x, p, t)
@log state = x
@log p # the same as `@log p = p`
dx .= -x
end