Nested Models

One of SimpleSim.jl's most powerful feature is the ability to structure models in hierarchies. In other words, every model in a simulation can have an arbitrary number of submodels.

This enables you to use SimpleSim.jl in a similar way to how you would draw a control block diagram.

For example, the closed-loop system consisting of a controller and a plant could be implemented as three systems:

  • the controller
  • the plant
  • a feedback system

The controller and the plant would then be subsystems of the "feedback sytem". The feedback system takes care of passing the current output of the plant to the controller and calling the plant with the current control input.

Creating Submodels

SimpleSim.jl accepts three types of collections to serve as a list of submodels:

  • Vector
  • Tuple
  • NamedTuple

Given three submodels model_1, model_2 and model_3 created as described in the chapters about continuous-time and discrete-time models, the creation of the submodels structure is shown below.

As a Vector:

my_submodels = [model_1, model_2, model_3]

As a Tuple:

my_submodels = (model_1, model_2, model_3)

As a NamedTuple:

my_submodels = (
    m_1 = model_1,
    m_2 = model_2,
    m_3 = model_3
)

Any of the above can be passed to the parent model as follows.

my_ct_model = (
    p = nothing,
    fc = fc_my_model,
    gc = gc_my_model,
    xc0 = my_initial_state,
    uc0 = my_initial_input,
    models = my_submodels,
)

Calling Submodels

Only the top-level passed to the simulate function is actively called by SimpleSim.jl. Submodels must be called by their respective parent model.

This is done using the @call! macro and can only be done from within the gc and gd functions. Calls from inside fc or fd are not allowed for technical reasons.

An example of how to call a submodel is given below.

function gc_my_model(x, u, p, t; models)
    my_submodel_input = # ...

    # if `models` is a Vector or Tuple
    y = @call! models[1] my_submodel_input

    # if `models` is a NamedTuple
    y = @call! models.m_1 my_submodel_input
end

After calling (and therefore updating) a submodel you can do whatever you need to do with its output. Note that SimpleSim.jl takes care of timing issues. Submodels are only updated if they are due. This is of course only relevant for discrete-time systems.

If you need to access the output of a submodel from inside a fc function, use the @out macro. It returns the current output of a submodel without updating it.

function fc_my_model(x, u, p, t; models)
    # if `models` is a Vector or Tuple
    y = @out models[1]

    # if `models` is a NamedTuple
    y = @out models.m_1
end

Several illustrative examples of nested simulations are given in the examples section.