Simulator#

class Simulator(model)[source]#

Bases: IteratedVariables

A class for simulating systems. Discrete-time and continuous systems can be considered.

Added in version >v4.5.1: New interface to settings. The class has an attribute settings which is an instance of SimulatorSettings or ContinousSimulatorSettings (please see this documentation for a list of available settings). Settings are now chosen as:

simulator.settings.t_step = 0.5

Previously, settings were passed to set_param(). This method is still available and wraps the new interface. The new method has important advantages:

  1. The simulator.settings attribute can be printed to see the current configuration.

  2. Context help is available in most IDEs (e.g. VS Code) to see the available settings, the type and a description.

do-mpc uses the CasADi interface to popular state-of-the-art tools such as Sundials CVODES for the integration of ODE/DAE equations.

Configuration and setup:

Configuring and setting up the simulator involves the following steps:

  1. Configure the simulator with SimulatorSettings or ContinousSimulatorSettings. The simulator instance has the attribute settings which is an instance of SimulatorSettings or ContinousSimulatorSettings.

  2. Set parameter function with get_p_template() and set_p_fun().

  3. Set time-varying parameter function with get_tvp_template() and set_tvp_fun().

  4. Setup simulator with setup().

During runtime, call the simulator make_step() method with current input (u). This computes the next state of the system and the respective measurement. Optionally, pass (sampled) random variables for the process w and measurement noise v (if they were defined in :py:class`do_mpc.model.Model`)

Parameters:

model (Model) – A configured and setup do_mpc.model.Model

Methods#

get_p_template#

get_p_template(self)#

Obtain output template for set_p_fun(). Use this method in conjunction with set_p_fun() to define the function for retrieving the parameters at each sampling time.

See set_p_fun() for more details.

Returns:

Union[SXStruct, MXStruct] – numerical CasADi structure

get_tvp_template#

get_tvp_template(self)#

Obtain the output template for set_tvp_fun(). Use this method in conjunction with set_tvp_fun() to define the function for retrieving the time-varying parameters at each sampling time.

Returns:

Union[SXStruct, MXStruct] – numerical CasADi structure

init_algebraic_variables#

init_algebraic_variables(self)#

Initializes the algebraic variables. Solve the algebraic equations for the initial values of x0, u0, p0, tvp0. Sets the results to z0 and returns them.

Note

The method internally calls set_initial_guess() to set the initial guess for the algebraic variables.

The initialization is computed by solving the algebraic model equations under consideration of the initial guess supplied in z0.

Example:

simulator = do_mpc.simulator.Simulator(model)

# Set initial value for the state:
simulator.x0 = np.array([0.1, 0.1]).reshape(-1,1)

# Obtain initial guess for the algebraic variables:
z0 = simulator.init_algebraic_variables()

# Initial guess is stored in simulator.z0 and simulator.set_initial_guess() was called internally.
Returns:

ndarray – Initial guess for the algebraic variables.

make_step#

make_step(self, u0=None, v0=None, w0=None)#

Main method of the simulator class during control runtime. This method is called at each timestep and computes the next state or the current control input u0. The method returns the resulting measurement, as defined in do_mpc.model.Model.set_meas.

The initial state x0 is stored as a class attribute. Use this attribute x0 to change the initial state. It is also possible to supply an initial guess for the algebraic states through the attribute z0 and by calling set_initial_guess().

Finally, the method can be called with values for the process noise w0 and the measurement noise v0 that were (optionally) defined in the do_mpc.model.Model. Typically, these values should be sampled from a random distribution, e.g. np.random.randn for a random normal distribution.

The method prepares the simulator by setting the current parameters, calls simulator.simulate() and updates the do_mpc.data object.

Parameters:
  • u0 (ndarray) – Current input to the system. Optional parameter for autonomous systems.

  • v0 (ndarray) – Additive measurement noise

  • w0 (ndarray) – Additive process noise

Returns:

ndarray – y_next

reset_history#

reset_history(self)#

Reset the history of the simulator.

Return type:

None

set_initial_guess#

set_initial_guess(self)#

Initial guess for DAE variables. Use the current class attribute z0 to create the initial guess for the DAE algebraic equations.

The simulator uses “warmstarting” to solve the continous/discrete DAE system by using the previously computed algebraic states as an initial guess. Thus, this method is typically only invoked once. :rtype: None

Warning

If no initial values for z0 were supplied during setup, they default to zero.

set_p_fun#

set_p_fun(self, p_fun)#

Method to set the function which gives the values of the parameters. This function must return a CasADi structure which can be obtained with get_p_template().

Example:

In the do_mpc.model.Model we have defined the following parameters:

Theta_1 = model.set_variable('parameter', 'Theta_1')
Theta_2 = model.set_variable('parameter', 'Theta_2')
Theta_3 = model.set_variable('parameter', 'Theta_3')

To integrate the ODE or evaluate the discrete dynamics, the simulator needs to obtain the numerical values of these parameters at each timestep. In the most general case, these values can change, which is why a function must be supplied that can be evaluted at each timestep to obtain the current values.

do-mpc requires this function to have a specific return structure which we obtain first by calling:

p_template = simulator.get_p_template()

The parameter function can look something like this:

p_template['Theta_1'] = 2.25e-4
p_template['Theta_2'] = 2.25e-4
p_template['Theta_3'] = 2.25e-4

def p_fun(t_now):
    return p_template

simulator.set_p_fun(p_fun)

which results in constant parameters.

A more “interesting” variant could be this random-walk:

p_template['Theta_1'] = 2.25e-4
p_template['Theta_2'] = 2.25e-4
p_template['Theta_3'] = 2.25e-4

def p_fun(t_now):
    p_template['Theta_1'] += 1e-6*np.random.randn()
    p_template['Theta_2'] += 1e-6*np.random.randn()
    p_template['Theta_3'] += 1e-6*np.random.randn()
    return p_template
Parameters:

p_fun (Callable[[float], Union[SXStruct, MXStruct]]) – A function which gives the values of the parameters

Raises:

assert – p must have the right structure

Return type:

None

set_param#

set_param(self, **kwargs)#

Warning

This method will be depreciated in a future version. Settings are available via the settings attribute which is an instance of ContinousSimulatorSettings or SimulatorSettings.

Note

A comprehensive list of all available parameters can be found in ContinousSimulatorSettings or SimulatorSettings.

For example:

simulator.settings.t_step = 0.5

The old interface, as shown in the example below, can still be accessed until further notice.

simulator.set_param(t_step=0.5)

Note

The only required parameters are t_step. All other parameters are optional.

Return type:

None

set_tvp_fun#

set_tvp_fun(self, tvp_fun)#

Method to set the function which returns the values of the time-varying parameters. This function must return a CasADi structure which can be obtained with get_tvp_template().

In the do_mpc.model.Model we have defined the following parameters:

a = model.set_variable('_tvp', 'a')

The integrate the ODE or evaluate the discrete dynamics, the simulator needs to obtain the numerical values of these parameters at each timestep. In the most general case, these values can change, which is why a function must be supplied that can be evaluted at each timestep to obtain the current values.

do-mpc requires this function to have a specific return structure which we obtain first by calling:

tvp_template = simulator.get_tvp_template()

The time-varying parameter function can look something like this:

def tvp_fun(t_now):
    tvp_template['a'] = 3
    return tvp_template

simulator.set_tvp_fun(tvp_fun)

which results in constant parameters.

Note

From the perspective of the simulator there is no difference between time-varying parameters and regular parameters. The difference is important only for the MPC controller and MHE estimator. These methods consider a finite sequence of future / past information, e.g. the weather, which can change over time. Parameters, on the other hand, are constant over the entire horizon.

Parameters:

tvp_fun (Callable[[float], Union[SXStruct, MXStruct]]) – Function which gives the values of the time-varying parameters

Raises:
  • assertion – tvp_fun has incorrect return type.

  • assertion – Incorrect output of tvp_fun. Use get_tvp_template to obtain the required structure.

Return type:

None

setup#

setup(self)#

Sets up the simulator and finalizes the simulator configuration. Only after the setup, the make_step() method becomes available.

Raises:

assertion – t_step must be set

Return type:

None

simulate#

simulate(self)#

Call the CasADi simulator.

Warning

simulate() can be used as part of the public API but is typically called from within make_step() which wraps this method and sets the required values to the sim_x_num and sim_p_num structures automatically.

Numerical values for sim_x_num and sim_p_num need to be provided beforehand in order to simulate the system for one time step:

  • states sim_c_num['_x']

  • algebraic states sim_z_num['_z']

  • inputs sim_p_num['_u']

  • parameter sim_p_num['_p']

  • time-varying parameters sim_p_num['_tvp']

The function returns the new state of the system.

Returns:

ndarray – x_new

Attributes#

settings#

Simulator.settings#

All necessary parameters for the simulator.

This is a core attribute of the Simulator class. It is used to set and change parameters when setting up the simulator by accessing an instance of SimulatorSettings or ContinousSimulatorSettings.

Example to change settings:

simulator.settings.t_step = 0.5

Note

Settings cannot be updated after calling do_mpc.simulator.setup().

For a detailed list of all available parameters see SimulatorSettings or ContinousSimulatorSettings.

t0#

Simulator.t0#

Current time marker of the class. Use this property to set of query the time.

Set with int, float, numpy.ndarray or casadi.DM type.

u0#

Simulator.u0#

Initial input and current iterate. This is the numerical structure holding the information about the current input in the class. The property can be indexed according to the model definition.

Example:

model = do_mpc.model.Model('continuous')
model.set_variable('_u','heating', shape=(4,1))

...
mhe = do_mpc.estimator.MHE(model)
# or
mpc = do_mpc.estimator.MPC(model)

# Get or set current value of variable:
mpc.u0['heating', 0] # 0th element of variable
mpc.u0['heating']    # all elements of variable
mpc.u0['heating', 0:2]    # 0th and 1st element

Useful CasADi symbolic structure methods:

  • .shape

  • .keys()

  • .labels()

x0#

Simulator.x0#

Initial state and current iterate. This is the numerical structure holding the information about the current states in the class. The property can be indexed according to the model definition.

Example:

model = do_mpc.model.Model('continuous')
model.set_variable('_x','temperature', shape=(4,1))

...
mhe = do_mpc.estimator.MHE(model)
# or
mpc = do_mpc.estimator.MPC(model)

# Get or set current value of variable:
mpc.x0['temperature', 0] # 0th element of variable
mpc.x0['temperature']    # all elements of variable
mpc.x0['temperature', 0:2]    # 0th and 1st element

Useful CasADi symbolic structure methods:

  • .shape

  • .keys()

  • .labels()

z0#

Simulator.z0#

Initial algebraic state and current iterate. This is the numerical structure holding the information about the current algebraic states in the class. The property can be indexed according to the model definition.

Example:

model = do_mpc.model.Model('continuous')
model.set_variable('_z','temperature', shape=(4,1))

...
mhe = do_mpc.estimator.MHE(model)
# or
mpc = do_mpc.estimator.MPC(model)

# Get or set current value of variable:
mpc.z0['temperature', 0] # 0th element of variable
mpc.z0['temperature']    # all elements of variable
mpc.z0['temperature', 0:2]    # 0th and 1st element

Useful CasADi symbolic structure methods:

  • .shape

  • .keys()

  • .labels()