-
-
Notifications
You must be signed in to change notification settings - Fork 40
Simulation results
sim.run(bd, T, ...) returns a BDStruct — a simple attribute/dict-hybrid container (out.foo and
out["foo"] both work) holding everything the run collected. print(out) gives a quick overview of
what's inside.
-
out.t—ndarray(samples), the simulation time at each recorded sample. -
out.x—ndarray(samples, nstates), the trajectory of every continuous-time state (fromContinuousBlocksubclasses) concatenated in diagram order. -
out.xnames— one name per column ofout.x, eg.'plant:x_0'.
If the diagram has one or more Clocks (see Discrete-time blocks), each clock
gets its own nested sub-struct on out, named after the clock (dots stripped from the name), holding:
-
.t— that clock's own tick times, generally a different, coarser grid than the top-levelout.t. -
.X— capital X —ndarray(ticks, ndstates), the trajectory of that clock's discrete/sampled state (fromSampledBlocksubclasses driven by it). -
.Xnames— one name per column of.X.
This is the .x vs .X distinction to watch for: lowercase x is continuous state, at the top
level of out. Uppercase X is discrete state, one level down inside each clock's own sub-struct —
not at the top level at all, and the case difference is significant. For example, with a clock named
"controller" driving one ZOH block and a continuous plant, print(out) shows:
controller::
X = ndarray:float64 (20, 1)
Xnames = ['zoh.0:X_0'] (list)
t = ndarray:float64 (20,)
t = ndarray:float64 (159,)
x = ndarray:float64 (159, 1)
xnames = ['plant:x_0'] (list)
Anything passed to the watch=[...] option of run() is logged separately from state:
out = sim.run(bd, T=5, watch=[demand, plant])-
out.y—ndarray(samples, n_watched_columns), every watched signal column-stacked into one array. A watched signal that's itself an array contributes one column per element. -
out.ynames— one name per column ofout.y, in the same order aswatch=[...]. Names default to the watched block/plug's fullrepr()unless it has a friendlyname=, so it's worth naming blocks you intend to watch.
Older code may use out.y0, out.y1, ... — one array per watched signal, from before they were
column-stacked into out.y. These still work but raise a DeprecationWarning pointing at the
replacement:
out.y0 is deprecated and will be removed in a future release; use out.y[:, 0] instead
(out.ynames gives the column order for multi-signal watches).
Replace out.yN with out.y[:, N].
out.stats (a hidden field, .stats, exposed via normal attribute access) holds counters from the
run: integration_time_points, run_interval_calls, ydot_calls, integrator_wall_time,
events_detected_total, and events_detected_by_source. The same numbers are what gets printed to
the console at the end of a run when quiet=False.
-
Running for
run()'s options, includingwatch=. - Discrete-time blocks for how clocks and discrete state work.
-
Home for the getting-started example that first shows
out.
Copyright (c) Peter Corke 2020-
- Home
- API reference (Sphinx)
- Block catalog
- Control Systems Magazine article
- Adding blocks to your model
- Block path
- Connecting blocks
- Compiling
- Running
- Watching a simulation variable
- Simulation results
- Runtime options
- Environment variables
- Discrete-time blocks
- Subsystems
- Figures
- Notebook animation
- Animation and movies
- PID control
- Coding patterns
- Block methods and attributes
- Time stepping: integration, animation & events
- Blocks, wires and plugs
- Graphics blocks
- Evaluation
- Runtimes and simulator state
- Creating a new block
- Related packages
Under development on feat/realtime branch, planned for release before end of 2026.