CATIA-Systems / FMPy

Simulate Functional Mockup Units (FMUs) in Python
Other
429 stars 118 forks source link

FMPy ignores the StartTime annotation when solving initial equations #638

Closed casella closed 8 months ago

casella commented 8 months ago

Please consider the following MWE:

model M
  Real x = time;
  parameter Real x0(fixed = false);
initial equation
  x0 = x;
equation
  annotation(experiment(StartTime = 0.2, StopTime = 1.0, Interval = 1e-2));
end M;

and generate an FMU with OMC. Dymola would do as well 😃

We simulated that with the following Python script:

from fmpy import *
fmu ='abc.fmu'
dump(fmu)  # get information
result = simulate_fmu(fmu)         # simulate the FMU
from fmpy.util import plot_result  # import the plot function
plot_result(result)                # plot the result

Apparently, the information about StartTime is correctly fetched from the generated FMU, since it is displayed correctly by dump(fmu). However, the initial equations are solved with time = 0 instead of time = 0.2, as it is apparent from the value of x0 in the simulation results.

This is causing a lot of trouble when testing models from the Buildings library such as Buildings.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation.FiniteLineSource_Integrand_Equivalent which have a non-zero StartTime, because it causes some functions to be initially called with zero input values, triggering all kind of exceptions.

@t-sommer, please fix that at your earliest convenience.

t-sommer commented 8 months ago

Note that a tool can ignore this information. From the FMI Spec. 2.0.3, p. 45

DefaultExperiment consists of the optional default start time, stop time, relative tolerance, and step size for the first simulation run. A tool may ignore this information.

If you require a specific start time, you should set it explicitly.

result = simulate_fmu(fmu, start_time=0.2)  # set start time explicitly
casella commented 8 months ago

@t-sommer thanks for pointing this out. However, FMPy indeed does take into account start time, stop time, relative tolerance, and step size. The simulation indeed starts at StartTime, as you can see from the results obtained by simulating the FMI obtained by the MWE above. It only ignores StartTime when solving the initial equations. I guess this inconsistent behaviour can be considered a bug that would be better be fixed 😃

That said, we need to simulate FMIs for which the information about StartTime is essential for the correctness of the simulation, as it impacts on the validity range of the simulation. That information is only contained in the modeldescription.xml file. If we want to find a workaround for this issue, how can we fetch that information using FMPy?

Thanks!

t-sommer commented 8 months ago

If I export the model with Dymola 2024x and run

from fmpy import *

result = simulate_fmu('M.fmu', output_interval=0.2, fmi_call_logger=print)

plot_result(result)

I get the following output

fmi2Instantiate(instanceName="M", fmuType=1, guid="{0badd4e1-6771-4d80-b985-28d34a8c7e4b}", resourceLocation="file:///C:/Users/tsr2/AppData/Local/Temp/tmp4hu8rwy3/resources", callbacks=fmi2CallbackFunctions(logger=0x000002BB50273D40, allocateMemory=0x000002BB50273D40, freeMemory=0x000002BB50273D40, stepFinished=0x000002BB50273D40, componentEnvironment=0x0), visible=0, loggingOn=0) -> 0x2bb4fe6c3b0
fmi2SetupExperiment(component=0x2bb4fe6c3b0, toleranceDefined=1, tolerance=0.0001, startTime=0.2, stopTimeDefined=1, stopTime=1.0) -> OK
fmi2EnterInitializationMode(component=0x2bb4fe6c3b0) -> OK
fmi2ExitInitializationMode(component=0x2bb4fe6c3b0) -> OK
fmi2GetReal(component=0x2bb4fe6c3b0, vr=[905969665, 100663296], nvr=2, value=[0.2, 0.2]) -> OK
fmi2DoStep(component=0x2bb4fe6c3b0, currentCommunicationPoint=0.2, communicationStepSize=0.2, noSetFMUStatePriorToCurrentPoint=1) -> OK
fmi2GetReal(component=0x2bb4fe6c3b0, vr=[905969665, 100663296], nvr=2, value=[0.4, 0.2]) -> OK
fmi2DoStep(component=0x2bb4fe6c3b0, currentCommunicationPoint=0.4, communicationStepSize=0.2, noSetFMUStatePriorToCurrentPoint=1) -> OK
fmi2GetReal(component=0x2bb4fe6c3b0, vr=[905969665, 100663296], nvr=2, value=[0.6000000000000001, 0.2]) -> OK
fmi2DoStep(component=0x2bb4fe6c3b0, currentCommunicationPoint=0.6000000000000001, communicationStepSize=0.2, noSetFMUStatePriorToCurrentPoint=1) -> OK
fmi2GetReal(component=0x2bb4fe6c3b0, vr=[905969665, 100663296], nvr=2, value=[0.8, 0.2]) -> OK
fmi2DoStep(component=0x2bb4fe6c3b0, currentCommunicationPoint=0.8, communicationStepSize=0.2, noSetFMUStatePriorToCurrentPoint=1) -> OK
fmi2GetReal(component=0x2bb4fe6c3b0, vr=[905969665, 100663296], nvr=2, value=[1.0, 0.2]) -> OK
fmi2Terminate(component=0x2bb4fe6c3b0) -> OK
fmi2FreeInstance(component=0x2bb4fe6c3b0)

newplot (1)

Can you point out the problem?

casella commented 8 months ago

In our case, we got x0 = 0.

Maybe there is some issue in the way OMC-generated FMUs handle initial equations. Would you mind running this FMU (renamed as zip) generated by OMC with the same script?

Thanks!

t-sommer commented 8 months ago

Here you go:

Dymola

from fmpy import *

result = simulate_fmu('M_Dymola.fmu', fmi_type='ModelExchange', output_interval=0.2, fmi_call_logger=print)

plot_result(result)
fmi2Instantiate(instanceName="M", fmuType=0, guid="{0badd4e1-6771-4d80-b985-28d34a8c7e4b}", resourceLocation="file:///C:/Users/tsr2/AppData/Local/Temp/tmpowuwruwk/resources", callbacks=fmi2CallbackFunctions(logger=0x000001FBC28F2EE0, allocateMemory=0x000001FBC28F2EE0, freeMemory=0x000001FBC28F2EE0, stepFinished=0x000001FBC28F2EE0, componentEnvironment=0x0), visible=0, loggingOn=0) -> 0x1fbc2440200
fmi2SetupExperiment(component=0x1fbc2440200, toleranceDefined=0, tolerance=0.0, startTime=0.2, stopTimeDefined=1, stopTime=1.0) -> OK
fmi2EnterInitializationMode(component=0x1fbc2440200) -> OK
fmi2ExitInitializationMode(component=0x1fbc2440200) -> OK
fmi2NewDiscreteStates(component=0x1fbc2440200, eventInfo=fmi2EventInfo(newDiscreteStatesNeeded=0, terminateSimulation=0, nominalsOfContinuousStatesChanged=0, valuesOfContinuousStatesChanged=0, nextEventTimeDefined=0, nextEventTime=0.0)) -> OK
fmi2EnterContinuousTimeMode(component=0x1fbc2440200) -> OK
fmi2GetReal(component=0x1fbc2440200, vr=[905969665, 100663296], nvr=2, value=[0.2, 0.2]) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.2) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.20000001332800377) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.20001632666760197) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.200008163333801) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.200008163333801) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.21600816333380102) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.23200816333380103) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.24800816333380105) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.26400816333380106) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.2800081633338011) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.2960081633338011) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.3120081633338011) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.3280081633338011) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.34400816333380113) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.36000816333380115) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.37600816333380116) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.3920081633338012) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.4080081633338012) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.4) -> OK
fmi2CompletedIntegratorStep(component=0x1fbc2440200, noSetFMUStatePriorToCurrentPoint=1, enterEventMode=0, terminateSimulation=0) -> OK
fmi2GetReal(component=0x1fbc2440200, vr=[905969665, 100663296], nvr=2, value=[0.4, 0.2]) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.4240081633338012) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.4400081633338012) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.45600816333380123) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.47200816333380125) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.48800816333380126) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.5040081633338013) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.5200081633338013) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.5360081633338013) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.5520081633338013) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.5680081633338013) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.5840081633338013) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.6000081633338014) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.6000000000000001) -> OK
fmi2CompletedIntegratorStep(component=0x1fbc2440200, noSetFMUStatePriorToCurrentPoint=1, enterEventMode=0, terminateSimulation=0) -> OK
fmi2GetReal(component=0x1fbc2440200, vr=[905969665, 100663296], nvr=2, value=[0.6000000000000001, 0.2]) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.6160081633338014) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.6320081633338014) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.6480081633338014) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.6640081633338014) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.6800081633338014) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.6960081633338014) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.7120081633338015) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.7280081633338015) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.7440081633338015) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.7600081633338015) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.7760081633338015) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.7920081633338015) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.8080081633338015) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.8) -> OK
fmi2CompletedIntegratorStep(component=0x1fbc2440200, noSetFMUStatePriorToCurrentPoint=1, enterEventMode=0, terminateSimulation=0) -> OK
fmi2GetReal(component=0x1fbc2440200, vr=[905969665, 100663296], nvr=2, value=[0.8, 0.2]) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.8240081633338016) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.8400081633338016) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.8560081633338016) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.8720081633338016) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.8880081633338016) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.9040081633338016) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.9200081633338016) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.9360081633338017) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.9520081633338017) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.9680081633338017) -> OK
fmi2SetTime(component=0x1fbc2440200, time=0.9840081633338017) -> OK
fmi2SetTime(component=0x1fbc2440200, time=1.0000081633338016) -> OK
fmi2SetTime(component=0x1fbc2440200, time=1.0) -> OK
fmi2CompletedIntegratorStep(component=0x1fbc2440200, noSetFMUStatePriorToCurrentPoint=1, enterEventMode=0, terminateSimulation=0) -> OK
fmi2GetReal(component=0x1fbc2440200, vr=[905969665, 100663296], nvr=2, value=[1.0, 0.2]) -> OK
fmi2Terminate(component=0x1fbc2440200) -> OK
fmi2FreeInstance(component=0x1fbc2440200)

newplot (2)

OpenModelica

from fmpy import *

result = simulate_fmu('M_OM.fmu', fmi_type='ModelExchange', output_interval=0.2, fmi_call_logger=print)

plot_result(result)
fmi2Instantiate(instanceName="M", fmuType=0, guid="{e9edaf6d-b6c2-4d6b-b097-20eac01209ba}", resourceLocation="file:///C:/Users/tsr2/AppData/Local/Temp/tmpngzs7iok/resources", callbacks=fmi2CallbackFunctions(logger=0x0000021C7FF63D40, allocateMemory=0x0000021C7FF63D40, freeMemory=0x0000021C7FF63D40, stepFinished=0x0000021C7FF63D40, componentEnvironment=0x0), visible=0, loggingOn=0) -> 0x21c6d0e83c0
fmi2SetupExperiment(component=0x21c6d0e83c0, toleranceDefined=0, tolerance=0.0, startTime=0.2, stopTimeDefined=1, stopTime=1.0) -> OK
fmi2EnterInitializationMode(component=0x21c6d0e83c0) -> OK
fmi2ExitInitializationMode(component=0x21c6d0e83c0) -> OK
fmi2NewDiscreteStates(component=0x21c6d0e83c0, eventInfo=fmi2EventInfo(newDiscreteStatesNeeded=0, terminateSimulation=0, nominalsOfContinuousStatesChanged=0, valuesOfContinuousStatesChanged=0, nextEventTimeDefined=0, nextEventTime=0.0)) -> OK
fmi2EnterContinuousTimeMode(component=0x21c6d0e83c0) -> OK
fmi2GetReal(component=0x21c6d0e83c0, vr=[0, 1], nvr=2, value=[0.0, 0.0]) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.2) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.20000001332800377) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.20001632666760197) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.200008163333801) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.200008163333801) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.21600816333380102) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.23200816333380103) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.24800816333380105) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.26400816333380106) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.2800081633338011) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.2960081633338011) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.3120081633338011) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.3280081633338011) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.34400816333380113) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.36000816333380115) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.37600816333380116) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.3920081633338012) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.4080081633338012) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.4) -> OK
fmi2CompletedIntegratorStep(component=0x21c6d0e83c0, noSetFMUStatePriorToCurrentPoint=1, enterEventMode=0, terminateSimulation=0) -> OK
fmi2GetReal(component=0x21c6d0e83c0, vr=[0, 1], nvr=2, value=[0.4, 0.0]) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.4240081633338012) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.4400081633338012) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.45600816333380123) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.47200816333380125) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.48800816333380126) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.5040081633338013) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.5200081633338013) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.5360081633338013) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.5520081633338013) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.5680081633338013) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.5840081633338013) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.6000081633338014) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.6000000000000001) -> OK
fmi2CompletedIntegratorStep(component=0x21c6d0e83c0, noSetFMUStatePriorToCurrentPoint=1, enterEventMode=0, terminateSimulation=0) -> OK
fmi2GetReal(component=0x21c6d0e83c0, vr=[0, 1], nvr=2, value=[0.6000000000000001, 0.0]) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.6160081633338014) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.6320081633338014) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.6480081633338014) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.6640081633338014) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.6800081633338014) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.6960081633338014) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.7120081633338015) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.7280081633338015) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.7440081633338015) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.7600081633338015) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.7760081633338015) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.7920081633338015) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.8080081633338015) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.8) -> OK
fmi2CompletedIntegratorStep(component=0x21c6d0e83c0, noSetFMUStatePriorToCurrentPoint=1, enterEventMode=0, terminateSimulation=0) -> OK
fmi2GetReal(component=0x21c6d0e83c0, vr=[0, 1], nvr=2, value=[0.8, 0.0]) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.8240081633338016) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.8400081633338016) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.8560081633338016) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.8720081633338016) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.8880081633338016) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.9040081633338016) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.9200081633338016) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.9360081633338017) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.9520081633338017) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.9680081633338017) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=0.9840081633338017) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=1.0000081633338016) -> OK
fmi2SetTime(component=0x21c6d0e83c0, time=1.0) -> OK
fmi2CompletedIntegratorStep(component=0x21c6d0e83c0, noSetFMUStatePriorToCurrentPoint=1, enterEventMode=0, terminateSimulation=0) -> OK
fmi2GetReal(component=0x21c6d0e83c0, vr=[0, 1], nvr=2, value=[1.0, 0.0]) -> OK
fmi2Terminate(component=0x21c6d0e83c0) -> OK
fmi2FreeInstance(component=0x21c6d0e83c0)

newplot (3)

casella commented 8 months ago

OK, then it is obviously the FMU generated by OMC that ignores StartTime when solving the initial equations. I already opened OpenModelica ticket #11855 on this topic.

Sorry for wasting you time with this 😅