Simulator#
- class Simulator(model)[source]#
Bases:
IteratedVariablesA 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
settingswhich is an instance ofSimulatorSettingsorContinousSimulatorSettings(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:The
simulator.settingsattribute can be printed to see the current configuration.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:
Configure the simulator with
SimulatorSettingsorContinousSimulatorSettings. The simulator instance has the attributesettingswhich is an instance ofSimulatorSettingsorContinousSimulatorSettings.Set parameter function with
get_p_template()andset_p_fun().Set time-varying parameter function with
get_tvp_template()andset_tvp_fun().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 processwand measurement noisev(if they were defined in :py:class`do_mpc.model.Model`)- Parameters:
model (
Model) – A configured and setupdo_mpc.model.Model
Methods#
get_p_template#
- get_p_template(self)#
Obtain output template for
set_p_fun(). Use this method in conjunction withset_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 withset_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 toz0and 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 indo_mpc.model.Model.set_meas.The initial state
x0is stored as a class attribute. Use this attributex0to change the initial state. It is also possible to supply an initial guess for the algebraic states through the attributez0and by callingset_initial_guess().Finally, the method can be called with values for the process noise
w0and the measurement noisev0that were (optionally) defined in thedo_mpc.model.Model. Typically, these values should be sampled from a random distribution, e.g.np.random.randnfor a random normal distribution.The method prepares the simulator by setting the current parameters, calls
simulator.simulate()and updates thedo_mpc.dataobject.- Parameters:
u0 (
ndarray) – Current input to the system. Optional parameter for autonomous systems.v0 (
ndarray) – Additive measurement noisew0 (
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
z0to 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.
Warning
If no initial values for
z0were supplied during setup, they default to zero.- Return type:
None
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.Modelwe 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)#
- Return type:
None
Warning
This method will be depreciated in a future version. Settings are available via the
settingsattribute which is an instance ofContinousSimulatorSettingsorSimulatorSettings.Note
A comprehensive list of all available parameters can be found in
ContinousSimulatorSettingsorSimulatorSettings.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.
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.Modelwe 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 withinmake_step()which wraps this method and sets the required values to thesim_x_numandsim_p_numstructures automatically.Numerical values for
sim_x_numandsim_p_numneed 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#
scaling#
- Simulator.scaling#
Get or set scaling factors for differential and algebraic states.
Scaling can significantly improve the numerical stability of the simulator, especially when differential and algebraic states have very different orders of magnitude. Variables are internally scaled (divided by the scaling factor) before integration, then scaled back to their physical values after integration. The scaled system is integrated.
Query and set scaling of the state variables. The
Simulator.scaling()method is an indexed property, meaning getting and setting this property requires an index and calls this function. The power index (elements are seperated by comas) must contain atleast the following elements:order
index name
valid options
1
variable type
_xand_z2
variable name
Names defined in
do_mpc.model.Model.Example:
# Set scaling for state variable 'x3' to 100 simulator.scaling['_x', 'x3'] = 100 # Set scaling for algebraic variable 'z1' to 0.001 simulator.scaling['_z', 'z1'] = 0.001 # Get current scaling value x3_scaling = simulator.scaling['_x', 'x3']
When to use scaling:
When state variables differ by several orders of magnitude
(e.g., temperatures ~300K and concentrations ~1e-6 mol/L) - When experiencing numerical difficulties
Note
Scaling factors must be set before calling simulator.setup().
setup(). Default scaling is 1.0 for all variables.
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
SimulatorSettingsorContinousSimulatorSettings.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
SimulatorSettingsorContinousSimulatorSettings.
t0#
- Simulator.t0#
Current time marker of the class. Use this property to set of query the time.
Set with
int,float,numpy.ndarrayorcasadi.DMtype.
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()