For example, this is my modelica model
model Test
Real x(start = 1000);
input Real k;
equation
der(x) = -k * x;
end Test;
from pyfmi import load_fmu
model = load_fmu('D:/FMU/Test.fmu')
model.initialize(0,10)
def f(time):
global model
x = model.get('x')
return time*0.1
inputs = ('k', f)
result = model.simulate(final_time=10, input=inputs)
How can I get the value of x in function f,or how can I get the value of the current state and then adjust the input parameters to continue the simulation? I tried to get it via model.get(), but failed. Thanks a lot for your help!
I would like to reframe your question a little bit as follows:
- Make simulation 1 and get the final value x and call it x1
- Reset (or reload) the model
- Enter the initial value of x as x1
- Optionally change the parameter k
- Make simulation 2 and get the final value x and call it x2
- Plot the two simulations after each other
The model I change to this
model Test
parameter Real k = 10;
parameter Real x_start = 1000;
Real x(start=x_start, fixed=true);
equation
der(x) = -k*x;
end Test;
And the script to this (and I use deprecated JModelica compiler to make the FMU, but instead the FMU can be made by for instance OpenModelica ver 1.25 or later)
# Setup framework
from pymodelica import compile_fmu
from pyfmi import load_fmu
import matplotlib.pyplot as plt
# Compile model
fmu_model = compile_fmu('Test','Test.mo', target='cs')
# Load model
model = load_fmu(fmu_model)
# Simulate
result1 = model.simulate(start_time=0, final_time=1)
x1 = model.get('x')
model.reset()
model.set('k', 1)
model.set('x_start',x1)
result2 = model.simulate(start_time=1, final_time=2)
x2 = model.get('x')
# Plot results
plt.figure()
plt.semilogy(result1['time'], result1['x'])
plt.semilogy(result2['time'], result2['x'])
plt.xlabel('Time'); plt.ylabel('x'); plt.grid()
plt.show()
Hope this address your main question?
Note, that the method can be easily generalised for continuous time systems. A dictionary of all the continuous time states can be obtained by the PyFMI-command
model.get_states_list()
For further inspiration on how to handle the more general case, take a look at the example on my Github page
https://colab.research.google.com/github/janpeter19/BPL_TEST2_Batch/blob/main/BPL_TEST2_Batch_colab.ipynb
run the example, and in cell 17 you see application of continued simulation after a parameter change. Also take a look at the Python setup file BPL_TEST2_Batch_explore.py
where you find the code for simu(,’cont’).
An alternative solution is to describe the values of parameter k over time in a table and then just have one simulation, instead of two. The advantage of changing the parameter from a table in Modelica is that the total execution time is slightly shorter. The drawback is if you have a large system with many parameter, then you need more tables. During explorative testing of impact of various parmeters of a larger model, the interactive command-line approach first described provides desired flexibility.
Modelica comes with Modelica Standard Library and here is a module called CombiTimeTable that I use in the further modified example below.
The updated model is
model TestWithTable
import Modelica.Blocks.Interfaces.RealInput;
import Modelica.Blocks.Sources;
import Modelica.Blocks.Types;
model Test
RealInput k;
parameter Real x_start = 1000;
Real x(start=x_start, fixed=true);
equation
der(x) = -k*x;
end Test;
Test test;
Sources.CombiTimeTable table(
smoothness=Types.Smoothness.ConstantSegments,
extrapolation = Types.Extrapolation.HoldLastPoint,
table=[0,10; 1,1]);
equation
connect(table.y[1], test.k);
end TestWithTable;
And the updated script is
# Setup framework
from pymodelica import compile_fmu
from pyfmi import load_fmu
import matplotlib.pyplot as plt
# Compile model
fmu_model = compile_fmu('TestWithTable','TestWithTable.mo', target='cs')
# Load model
model = load_fmu(fmu_model)
# Simulate
result = model.simulate(start_time=0, final_time=2)
# Plot results
plt.figure()
plt.semilogy(result['time'], result['test.x'])
plt.xlabel('Time'); plt.ylabel('x'); plt.grid()
plt.show()
I hope you like it!
You can read more about the MSL CombiTimeTable here
https://doc.modelica.org/Modelica%204.0.0/Resources/helpDymola/Modelica_Blocks_Sources.html#Modelica.Blocks.Sources.CombiTimeTable
And you can see an example of the usage of CombiTimeTable on my Github page https://github.com/janpeter19/BPL_TEST2_Chemostat
by running the example.
For the sake of completeness, I show a third solution below. It is more closer related to your own attempt using “input =” in the simulation call. The same model as in the first solution is used. The script is as follows.
# Setup framework
from pymodelica import compile_fmu
from pyfmi import load_fmu
import matplotlib.pyplot as plt
import numpy as np
# Compile model
fmu_model = compile_fmu('Test','Test.mo', target='cs')
# Load model
model = load_fmu(fmu_model)
# k varriation
eps = 0.05
k_data = np.transpose(np.vstack([np.array([0,1-eps, 1+eps, 2]), np.array([10, 10, 1, 1])]))
# Simulate
result = model.simulate(input=('k', k_data) , start_time=0, final_time=2)
# Plot results
plt.figure()
plt.semilogy(result['time'], result['x'])
plt.xlabel('Time'); plt.ylabel('x'); plt.grid()
plt.show()
Note that the k_data is interpreted using linear interpolation and here I choose to make a change between time 1-eps to 1+eps and you can see in the result above that the change of the k-parameter is not as abrupt as in the two previous solutions.