# [ANN] SimulationLogger.jl: a convenient logging tool for DifferentialEquations.jl

NOTE

1. This package is now being registered in General.
2. 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
# 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
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 have `return` 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
``````
6 Likes

(Update)

• annoying `return` convention is now resolved.
• v0.1.0 is released.
• v0.1.1 is being registered (including bug fixes).