Model Exchange FMI 2.0 Integration in PSCAD #2189
|
I am using an FMI 2.0 Model Exchange FMU exported from Simulink R2024b. I serialize the FMU state using fmi2GetFMUstate and fmi2SerializeFMUstate, and later restore it using fmi2DeSerializeFMUstate and fmi2SetFMUstate. This works correctly for a simple FMU, but not for a more complex one. After restoring the state, the simulation breaks, even though the inputs are identical. I have verified that the serialized state is restored successfully (all FMI functions return fmi2OK), and the same wrapper code works with another FMU. Has anyone experienced a similar issue with Simulink-generated Model Exchange FMUs? Are there any known limitations regarding state serialization/restoration, persistent variables, or additional FMI calls required after fmi2SetFMUstate? Thank you very much in advance. Best regards, Victor Sanchez Suarez I copy and paste the C-code I use to integrate the FMU: // Begin of DllLink_C #define REAL(param) double * param // Dependencies #define INSTANTIATION_TOKEN "{fc0f3c22-5a57-47b4-cf5a-df1b4f4ff486}" #define MAX_INSTANCES 1000 /* Inputs Value References type: fmi2ValueReference */ /* Sizes of Inputs */ /* Outputs Value References type: fmi2ValueReference */ /* Sizes of Outputs */ typedef struct typedef struct } FMUInstance; static FMUInstance instances[MAX_INSTANCES]; static FMUInstance* getContext(int instanceId) } static void cb_logMessage( } static void* cb_allocateMemory(size_t nobj, size_t size) static void cb_freeMemory(void* obj) static void loadFMU(FMUInstance* ctx) } static void init } static void step } static void param(FMUInstance* ctx, int* code, double* value, int* valueSize) } static void save(FMUInstance* ctx, fmi2Byte* buffer, size_t maxSize) } static void restore(FMUInstance* ctx, const fmi2Byte* buffer) } static void terminate(FMUInstance* ctx) } // -------------------------------------------------------------------------------------- void FUNCTION(func_main)(REAL(P), REAL(idc), REAL(Q), REAL(Vpu), REAL(PF), REAL(deltaVpu), REAL(deltaf), REAL(deltaUpu), REAL(PdroopK), REAL(QdroopK), REAL(P0), REAL(Q0), REAL(ZeroInjectionRef), REAL(PWM_Main), REAL(PWM_Aux), REAL(CTRL), REAL(PLL), REAL(SYNC), REAL(FOLLOW), REAL(QMODE), REAL(chg_fbd), REAL(dchg_fbd), REAL(PMODE), REAL(ZI), REAL(ZI_COMMS_LOST), REAL(STUDY), REAL(K1), REAL(K2), REAL(T_IGBT1), REAL(T_IGBT2), REAL(T_trf), REAL(Trf_High_T), REAL(u2), REAL(iC), REAL(i1), REAL(i3), REAL(udc), REAL(idc1), REAL(uSyncExt), REAL(uSyncInt), REAL(iNeutral), REAL(uUPS), REAL(iG), REAL(GatesMain), REAL(GatesAux), INTEGER(INSTANCE_ID), INTEGER(STATUS), INTEGER(RUNCTRL), INTEGER(NSTORI), INTEGER_ARR(STORI)) const int BUFFER_SIZE = 43749; int nstori = *NSTORI - 1; FMUInstance* ctx = getContext(*INSTANCE_ID); if (!ctx) buffer = (void*)(&(STORI[nstori])); if (*STATUS == INITIALIZE) if (*RUNCTRL) { step(ctx, P, idc, Q, Vpu, PF, deltaVpu, deltaf, deltaUpu, PdroopK, QdroopK, P0, Q0, ZeroInjectionRef, PWM_Main, PWM_Aux, CTRL, PLL, SYNC, FOLLOW, QMODE, chg_fbd, dchg_fbd, PMODE, ZI, ZI_COMMS_LOST, STUDY, K1, K2, T_IGBT1, T_IGBT2, T_trf, Trf_High_T, u2, iC, i1, i3, udc, idc1, uSyncExt, uSyncInt, iNeutral, uUPS, iG, GatesMain, GatesAux); } if (*STATUS == FINALIZE) (*NSTORI) += BUFFER_SIZE; // ------------------------------------------------------------ } // ------------------------------------------------------------ } |
Replies: 4 comments
|
You seem to test many different things at the same time: getting and setting the serialized FMU state for an FMU exported from Simulink, and an own implementation ...
|
|
I agree with @mohammadrezwankhan in that the current code base seems all over the place, to the point that any problems with the FMU - if any - would be masked by problems in the code itself. Beside the problems already mentioned, there are various other basic issues:
Besides, since the intent seems to be to just step through the simulation, why not employ a co-simulation FMU, which Simulink is just as capable of producing, and which is much easier to handle with the basic approach the code seems to want to employ. |
|
Thank you all very much for your detailed comments and suggestions. They were extremely helpful. You were right that there were several important issues in my importer implementation. Your feedback helped me review the code, fix a number of mistakes, and end up with a much more robust implementation. In particular, I corrected the handling of nextEventTime, time, and some other importer-side state. In the end, I managed to solve the problem. The root cause was actually inside the Simulink model I was exporting. An internal variable was not being initialized correctly, which led to inconsistent behavior after restoring the FMU state. Once that was fixed, state serialization and restoration worked correctly. Regarding the Model Exchange implementation, my workflow does not use fmi2GetContinuousStates, fmi2GetDerivatives, fmi2SetContinuousStates, fmi2GetEventIndicators, or fmi2CompletedIntegratorStep because this particular model is purely discrete and contains no continuous states or continuous-time variables. As for using a Co-Simulation FMU, that was actually one of my first approaches. Unfortunately, I encountered state management issues with Simulink-generated Co-Simulation FMUs, even when testing them within the Simulink environment itself. That is why I eventually decided to switch to Model Exchange. Once again, thank you all for taking the time to help. The quality of the feedback has been excellent, and it greatly helped me identify and resolve the problem. |
|
Thanks for closing the loop. For future readers, the resolution was two-part:
Because this particular FMU is purely discrete, a continuous-state integration loop is not required. Correct discrete-event handling and wrapper-state rollback are still required. The useful diagnostic sequence is therefore: first validate the FMU snapshot with an independent importer, then compare every importer-owned clock/event field across save and restore, and finally audit model initialization for state that is not deterministic at the restored point. |
Thanks for closing the loop. For future readers, the resolution was two-part:
nextEventTimeDefinedflag alongside the serialized FMU bytes. It must also preserve the FMI 2.0 Model Exchange state-machine and event-iteration rules.fmi2SetFMUstate. Correcting the initialization made serialization and restoration reproducible.Because this p…