TLDR: Place the
@log macro in your DifferentialEquations simulation to log intermediate variables.
One of the killer features of Simulink is the ability to log or plot (with a scope) any intermediate signal in a simulation with a click. It’s one of the things that I’ve been sorely missing in my DifferentialEquations.jl simulations. ModelingToolkit gives this ability, but it would be nice to have for non-ModelingToolkit simulations as well.
So let me introduce SimulationLogs.jl. By simply placing the macro
@log before any variable declaration in your simulation, you can now access that variable by calling
get_log on your solution object. Logged variables can also be plotted by calling the
scope function on your solution with the desired variables to be plotted.
For a pretty (I hope) illustrative example, see the PI cruise control example in the docs where I use the
scope function to inspect intermediate variables like so:
I’m going to post the FAQ section of the README here in order to head off any of the questions I anticipate people having:
How does this work with time stepping and variable caches and all that?
Despite the name,
@log doesn’t actually log anything while the simulation is running. The “logging” happens by calculating values from the stored solutions when
get_log is called.
Wait, how does that work?
There is a global
SimulationLog that is turned off by default. When it is off, the
@log macro basically doesn’t do anything. The
get_log function turns on the global log and then calls your simulation function (derivative function, vector field… whatever you want to call it) for each time point (these can be supplied, but will default to the saved time points). A copy of the global simulation log is passed as an output to the user, after which the global log then gets erased and turned back off.
Will logging variables slow my simulation down?
Nope. There is no runtime overhead because no logging is actually happening during the simulation.
How does this work when the same
@log gets called multiple times in the same time step (e.g. in a subfunction that gets called more than once)?
It doesn’t. Don’t do that. There are some good ways we could handle this, but they aren’t implemented right now.
What if my parameters are changed during the simulation or my simulation depends on some changing global state?
Then this probably won’t work.