modelicafmipyfmi

How to dynamically get the value of a variable in pyfmi


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!


Solution

  • I would like to reframe your question a little bit as follows:

    1. Make simulation 1 and get the final value x and call it x1
    2. Reset (or reload) the model
    3. Enter the initial value of x as x1
    4. Optionally change the parameter k
    5. Make simulation 2 and get the final value x and call it x2
    6. 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()
    

    enter image description here

    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').

    Here is in the FMI-standard a procedure to get and set the total state of an FMU, i.e. including both continuous and discrete time states and more.

    model.get_fmu_state()
    model.set_fmu_state()
    

    However, the FMUs generated by OpenModelica (or deprecated JModelica) does not support this functionality. Therefore, the approach described above has a place.