Interactive Solution Inspection
An interactive solution inspection tool based on WGLMakie and Bonito is provided through the helper package NetworkDynamicsInspector
.
First, we need to define the system we want to inspect.
Define some network, simulate it and get a solution object
using NetworkDynamics
using NetworkDynamicsInspector
using OrdinaryDiffEqTsit5
using Graphs
include(joinpath(pkgdir(NetworkDynamics), "test", "ComponentLibrary.jl"))
function get_sol(;limit=1.0)
g = SimpleGraph([0 1 1 0 1;
1 0 1 1 0;
1 1 0 1 0;
0 1 1 0 1;
1 0 0 1 0])
vs = [Lib.swing_mtk() for _ in 1:5];
set_default!(vs[1], :Pmech, -1)
set_default!(vs[2], :Pmech, 1.5)
set_default!(vs[3], :Pmech, -1)
set_default!(vs[4], :Pmech, -1)
set_default!(vs[5], :Pmech, 1.5)
ls = [Lib.line_mtk() for _ in 1:7];
nw = Network(g, vs, ls)
sinit = NWState(nw)
s0 = find_fixpoint(nw)
set_defaults!(nw, s0)
# set_position!(vs[1], (0.0, 0.0))
set_marker!(vs[1], :dtriangle)
set_marker!(vs[2], :utriangle)
set_marker!(vs[3], :dtriangle)
set_marker!(vs[4], :dtriangle)
set_marker!(vs[5], :utriangle)
cond = ComponentCondition([:P, :₋P, :srcθ], [:limit, :K]) do u, p, t
abs(u[:P]) - p[:limit]
end
affect = ComponentAffect([],[:active]) do u, p, ctx
@info "Trip line $(ctx.eidx) between $(ctx.src) and $(ctx.dst) at t=$(ctx.t)"
p[:active] = 0
end
cb = ContinousComponentCallback(cond, affect)
set_callback!.(ls, Ref(cb))
tripfirst = PresetTimeComponentCallback(1.0, affect) # reuse the same affect
add_callback!(nw[EIndex(5)], tripfirst)
nwcb = NetworkDynamics.get_callbacks(nw);
s0 = NWState(nw)
s0.p.e[:, :limit] .= limit
prob = ODEProblem(nw, uflat(s0), (0,6), copy(pflat(s0)), callback=nwcb)
sol = solve(prob, Tsit5())
end
sol = get_sol()
[ Info: Trip line 5 between 2 and 4 at t=1.0
[ Info: Trip line 7 between 4 and 5 at t=2.2476763961078197
[ Info: Trip line 4 between 2 and 3 at t=2.5025231919763837
[ Info: Trip line 1 between 1 and 2 at t=3.1947647110381228
[ Info: Trip line 3 between 1 and 5 at t=3.3380528681625083
[ Info: Trip line 2 between 1 and 3 at t=3.40426964599924
Now that we have an ODESolution
sol
, we can call inspect
to open the inspector GUI. The docstring provides several options to customize how the app is displayed.
inspect(sol; reset=true)
[ Info: New GUI Session started
Programmatic Access and GUI State Manipulation
Internally, the NetworkDynamicsInspector
maintains a global reference to an AppState
object. This AppState reflects changes made to the GUI by the user and can also be modified programmatically.
See the NetworkDynamicsInspector API for a complete list of available functions. A good starting point is the dump_app_state
function, which helps you recreate a GUI state that was previously configured manually.
Let's say we've adjusted the AppState to include an additional time series plot for the node states.
┌ Error: ts599: Plotting failed for idx Union{EIndex{Int64, Symbol}, VIndex{Int64, Symbol}}[VIndex(1, :θ), VIndex(1, :ω), VIndex(2, :θ), VIndex(2, :ω), VIndex(3, :θ), VIndex(3, :ω), VIndex(4, :θ), VIndex(4, :ω), VIndex(5, :θ), VIndex(5, :ω)]
│ e =
│ Can't get three: Screen Session uninitialized. Not yet displayed? Session status: UNINITIALIZED, id: 8b5a84e7-cdd1-4bba-ac5e-614ac26d8c87
│ Plot needs to be displayed to insert additional plots
└ @ NetworkDynamicsInspector ~/work/NetworkDynamics.jl/NetworkDynamics.jl/NetworkDynamicsInspector/src/timeseries.jl:401
We can dump the code which helps us to recreate the app state:
dump_app_state()
# To recreate the current state, run the following commands:
set_sol!(sol) # optional if after inspect(sol)
set_state!(; t=1.75, tmin=0.0, tmax=6.0)
set_graphplot!(; nstate=[:θ], estate=[:₋P], nstate_rel=false, estate_rel=false, ncolorrange=(-8.428582f0, 8.428582f0), ecolorrange=(-0.98994404f0, 0.98994404f0))
define_timeseries!([
(; selcomp=[VIndex(1), VIndex(2), VIndex(3), VIndex(4), VIndex(5)], states=[:θ, :ω], rel=false),
(; selcomp=[EIndex(1), EIndex(2), EIndex(3), EIndex(4), EIndex(5), EIndex(6), EIndex(7)], states=[:P], rel=false),
])
Now we can use this code to recreate the app state even though we've reseted it.
inspect(sol; reset=true)
"copy-paste and execute code returned by `dump_app_state` here"