Skip to content

Evaluation

Peter Corke edited this page Jun 7, 2021 · 3 revisions

Execution plan

At compile time the block diagram is analysed to create an execution plan. The result is a list of lists of blocks. The index of the outer list is the execution step, and all blocks in the inner list can be executed knowing their inputs have been computed in previous execution steps. The plan can be shown by:

  • bd.plan_print() as a formatted table
  • bd.plan_dotfile() as a GraphViz graph, for example

Execution plan

This is a data flow graph and arcs represent data dependencies. Each column of this graph represents an execution step. All blocks executed in the same step could be executed in parallel since they are not dependent on each other.

Step 0 of the plan includes all blocks whose output values are already known:

  • the outputs of the Source blocks such as constants or waveform generators
  • the outputs of the stateful Transfer blocks which are a function of their state

The plan is executed/evaluated by

bd.evaluate_plan(x, t, checkfinite=True, debuglist=[], sinks=True)

where x is the integrator's state vector, and t is current simulation time.

Each block, apart from source blocks, has an input list whose elements correspond to its input ports. During execution the block's output method is called and it computes its output as a function of the input values and time. The result is a list whose elements correspond to the block's output ports which the method returns. evaluate_plan copies the elements of that list to elements of the input list of the blocks it is connected to, as dictated by the block diagram's wiring network.

Integration

By default the scipy Runge-Kutta 45 algorithm is used. This is a variable step integrator which evaluates the state derivative multiple times before it converges on an estimate of x(t). The main steps are:

  1. Get the initial state vector. Each stateful blocks holds its own initial state, the getstate0 method of these blocks is called and the values concatenated to create x0.
  2. The integrator is initialised and it computes the state trajectory by making multiple requests for the derivative of the system state

Determining derivatives

Each derivative calculation requires evaluating the entire network for a particular state vector provided by the integrator. The steps are:

  1. Split the state vector across the stateful blocks and tell each block its state value using its setstate method
  2. Evaluate the entire block diagram.
  3. The the state derivative is computed by the values at the inputs to the stateful blocks. Each block returns its state derivative by its deriv method.

Although block execution is sequential, as far as the integrator is concerned this is an atomic operation, occurring at a single point in simulation time.

The state vector

Numerical integration updates a 1D state vector. Each stateful block is concerned with a part of the state vector:

  • before the integration begins the getstate0 method of each TransferBlock subclass block is called to return its initial state vector. This is typically handled by a default method in the TransferBlock class from the instance variable _x0. The initial state for the integration is the concatenation of all these individual state vectors.

  • for every evaluation the current state from the integrator is transfer to each TransferBlock subclass block via its setstate method. This method takes the required number of state elements from the left of the passed vector and returns the remainder. This is typically handled by a default method in the TransferBlock class and the block state is saved in the instance variable _x.