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

**URL:** <https://discourse.julialang.org/t/ann-simulationlogger-jl-a-convenient-logging-tool-for-differentialequations-jl/63182>\
**Category:** Package Announcements\
**Tags:** diffeq, logging\
**Created:** [June 19, 2021, 1:12pm UTC](https://discourse.julialang.org/t/ann-simulationlogger-jl-a-convenient-logging-tool-for-differentialequations-jl/63182 "2021-06-19T13:12:40Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![iHany](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihany/32/18151_2.png) [@iHany](https://discourse.julialang.org/u/iHany)\
**Post date:** [June 19, 2021, 1:12pm UTC](https://discourse.julialang.org/t/ann-simulationlogger-jl-a-convenient-logging-tool-for-differentialequations-jl/63182/1 "2021-06-19T13:12:40Z")

</div>

**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](https://github.com/JinraeKim/SimulationLogger.jl).  
This package provides convenient logging macros compatible with [DifferentialEquations.jl](https://diffeq.sciml.ai/stable/). Thanks to @jonniedie as the basic form of this package is based on [SimulationLogs.jl](https://github.com/jonniedie/SimulationLogs.jl).

Although this package is not yet mature, from several discussions such as [the original question](https://discourse.julialang.org/t/differentialequations-jl-saving-data-without-redundant-calculation-of-control-inputs/62559/3) and [the idea of this package](https://discourse.julialang.org/t/make-a-variable-as-a-global-variable-within-a-function/63067/21), I found that this functionality would also be convenient for other users.

The following examples and instruction are borrowed from the original [README.md](https://github.com/JinraeKim/SimulationLogger.jl).

Have a nice day!

# TL; DR: example code

## Example 1: typical usage (see [FlightSims.jl](https://github.com/JinraeKim/FlightSims.jl) for details)

```julia
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

```

 ![x_lqr](https://global.discourse-cdn.com/julialang/original/3X/a/b/ab6f29d85eb73a6a21f24c69f523733b29be3737.png)  
 ![u_lqr](https://global.discourse-cdn.com/julialang/original/3X/1/0/108571d5ca7c0e105008a415457a0e030dea073d.png)

## Example 2: low-level usage

```julia
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

```

 ![state](https://global.discourse-cdn.com/julialang/original/3X/9/4/945465a01e737e5039b1ecd38fa328098dc3aa2a.png)

# Main macros

## `@Loggable`

`@Loggable` is a macro that makes an ODE function _loggable_.

### Example

```julia
@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,

```julia
@Loggable function dynamics!(dx, x, p, t)
    dx .= -x
end

```

is good.

```julia
@Loggable dynamics! = (dx, x, p, t) -> dx .= -x

```

may not work properly.

- Functions annotated by `@Loggable` **MUST NOT** have `return` keyword. For example,

```julia
@Loggable function dynamics!(dx, x, p, t)
    dx .= -x
    nothing
end

```

works fine, but the logging functionality with `return`, for example,

```julia
@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

```julia
@Loggable function dynamics!(dx, x, p, t)
    @log state = x
    @log p # the same as `@log p = p`
    dx .= -x
end

```

---

<div class="post-metadata">

**Author:** ![iHany](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihany/32/18151_2.png) [@iHany](https://discourse.julialang.org/u/iHany)\
**Post date:** [June 22, 2021, 3:21pm UTC](https://discourse.julialang.org/t/ann-simulationlogger-jl-a-convenient-logging-tool-for-differentialequations-jl/63182/2 "2021-06-22T15:21:49Z")

</div>

(Update)

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