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:
FAQs
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.