In some interactive simulation work it is convenient to simulate up to a time point, change some parameters, and then continue simulation.
Using FMU for simulation then you need to reload or reset the FMU, and then enter new initial values for the state of the system that is the final values of the states from the previous simulation. You also need to enter the values for parameters that are different from default values in a similar way as for the previous simulation.
When you use PyFMI there is a convenient associated function get_states_list() as:
from pyfmi import load_fmu
...
model = load_fmu('file of fmu')
stateDict = model.get_states_list()
The dictionary stateDict contains the continuous time states and this is very helpful to further automate the update of the initial state values with the final state values from the previous simulation. The possible discrete time states is not included though, and needs to be handled manually, as far as I understand.
How do I get a similar dictionary of (continuous time) states using FMPy? Or even better, include also discrete time states?
With FMPy you can extract a list of the continuous states from the model description by following the derivative
attribute of the unknowns in ModelStructure/Derivatives
(see FMI Specification 2.0.4, p.57 & p. 61).
from fmpy import *
fmu_filename = 'MyModel.fmu'
model_description = read_model_description(fmu_filename)
continuous_states = \
[unknown.variable.derivative for unknown in model_description.derivatives]
Note that it is not allowed to set the values of variables with causality local
(see FMI Specification 2.0.4, p. 108). This includes the continuous states of a Co-Simulation FMU.
To retrieve and restore the state of a Co-Simulation FMU you have to use the FMU State API (see FMI Specification 2.0.4, p. 26).
# this script demonstrates how to pause and continue a simulation
# with FMPy (https://github.com/CATIA-Systems/FMPy) using get/set FMU state
from fmpy import *
from shutil import rmtree
import numpy as np
# FMI 2.0 BouncingBall model from the Reference FMUs v0.0.22
# https://github.com/modelica/Reference-FMUs/releases/download/v0.0.22/Reference-FMUs-0.0.22.zip
fmu_filename = 'BouncingBall.fmu'
model_description = read_model_description(fmu_filename)
if not model_description.coSimulation.canGetAndSetFMUstate:
raise Exception("The FMU does not support get/set FMU state.")
unzipdir = extract(fmu_filename)
# instantiate the FMU before simulating it, so we can keep it alive
fmu_instance = instantiate_fmu(
unzipdir=unzipdir,
model_description=model_description,
)
# simulation time when we pause the simulation
pause_time = 0.75
# use the step_finished callback to stop the simulation at pause_time
def step_finished(time, recorder):
""" Callback function that is called after every step """
return time < pause_time # stop the simulation early when time >= 2
# simulate up to pause_time
result1 = simulate_fmu(
filename=unzipdir,
model_description=model_description,
fmu_instance=fmu_instance,
output_interval=0.05,
terminate=False,
step_finished=step_finished
)
# retrieve the FMU state
fmu_state = fmu_instance.getFMUState()
# continue the simulation
result2 = simulate_fmu(
filename=unzipdir,
model_description=model_description,
fmu_instance=fmu_instance,
start_time=pause_time,
output_interval=0.05,
fmu_state=fmu_state
)
# combine and plot the results
plot_result(np.concatenate((result1, result2)), events=True, markers=True)
# clean up
fmu_instance.freeInstance()
rmtree(unzipdir, ignore_errors=True)