Skip to content

Commit

Permalink
Add synchronous features to C FMUs (#7155)
Browse files Browse the repository at this point in the history
* Changing parameter i to clockIndex for synchronous functions

* Check timers for synchronous features in FMUs

  - Changed getNextSampleTimeFMU to return a bool if next samlpe time is defined.
    Using -1 as next time and hoping that this will never be a real used time is not good enough.
  - Adding handleTimersFMI to handle clocks for FMI

* Fixing synchronous.c defines

* Adding test cases for clocked FMUs

* Disabeling FMI 1.0 tests
  • Loading branch information
AnHeuermann committed Feb 22, 2021
1 parent dc735fb commit d66fc7b
Show file tree
Hide file tree
Showing 30 changed files with 539 additions and 83 deletions.
24 changes: 12 additions & 12 deletions OMCompiler/Compiler/Template/CodegenC.tpl
Expand Up @@ -181,8 +181,8 @@ end translateModel;
extern int <%symbolName(modelNamePrefixStr,"setInputData")%>(DATA *data, const modelica_boolean file);
extern int <%symbolName(modelNamePrefixStr,"getTimeGrid")%>(DATA *data, modelica_integer * nsi, modelica_real**t);
extern void <%symbolName(modelNamePrefixStr,"function_initSynchronous")%>(DATA * data, threadData_t *threadData);
extern void <%symbolName(modelNamePrefixStr,"function_updateSynchronous")%>(DATA * data, threadData_t *threadData, long i);
extern int <%symbolName(modelNamePrefixStr,"function_equationsSynchronous")%>(DATA * data, threadData_t *threadData, long i);
extern void <%symbolName(modelNamePrefixStr,"function_updateSynchronous")%>(DATA * data, threadData_t *threadData, long clockIndex);
extern int <%symbolName(modelNamePrefixStr,"function_equationsSynchronous")%>(DATA * data, threadData_t *threadData, long clockIndex);
extern void <%symbolName(modelNamePrefixStr,"read_input_fmu")%>(MODEL_DATA* modelData, SIMULATION_INFO* simulationData);
extern void <%symbolName(modelNamePrefixStr,"function_savePreSynchronous")%>(DATA *data, threadData_t *threadData);
extern int <%symbolName(modelNamePrefixStr,"inputNames")%>(DATA* data, char ** names);
Expand Down Expand Up @@ -372,15 +372,15 @@ template functionUpdateSynchronous(list<ClockedPartition> clockedPartitions, Str
<<
<%auxFunction%>
/* Update the base clock. */
void <%symbolName(modelNamePrefix,"function_updateSynchronous")%>(DATA *data, threadData_t *threadData, long i)
void <%symbolName(modelNamePrefix,"function_updateSynchronous")%>(DATA *data, threadData_t *threadData, long clockIndex)
{
TRACE_PUSH
<%varDecls%>
modelica_boolean ret;
switch (i) {
switch (clockIndex) {
<%body%>
default:
throwStreamPrint(NULL, "Internal Error: unknown base partition %ld", i);
throwStreamPrint(NULL, "Internal Error: unknown base partition %ld", clockIndex);
break;
}
TRACE_POP
Expand All @@ -397,10 +397,10 @@ match baseClock
let si = daeExp(startInterval, contextOther, &preExp, &varDecls, &auxFunction)
<<
<%preExp%>
if (data->simulationInfo->clocksData[i].cnt > 0)
data->simulationInfo->clocksData[i].interval = data->localData[0]->timeValue - data->simulationInfo->clocksData[i].timepoint;
if (data->simulationInfo->clocksData[clockIndex].cnt > 0)
data->simulationInfo->clocksData[clockIndex].interval = data->localData[0]->timeValue - data->simulationInfo->clocksData[clockIndex].timepoint;
else
data->simulationInfo->clocksData[i].interval = <%si%>;
data->simulationInfo->clocksData[clockIndex].interval = <%si%>;
>>
else
let &preExp = buffer ""
Expand All @@ -415,7 +415,7 @@ match baseClock
'ModelicaMessage("Using default Clock(1.0)!");'
<<
<%preExp%>
data->simulationInfo->clocksData[i].interval = <%interval%>;
data->simulationInfo->clocksData[clockIndex].interval = <%interval%>;
<%warning%>
>>
end updatePartition;
Expand All @@ -439,15 +439,15 @@ template functionSystemsSynchronous(list<SubPartition> subPartitions, String mod
<%systs%>

/*Clocked systems equations */
int <%symbolName(modelNamePrefix,"function_equationsSynchronous")%>(DATA *data, threadData_t *threadData, long i)
int <%symbolName(modelNamePrefix,"function_equationsSynchronous")%>(DATA *data, threadData_t *threadData, long clockIndex)
{
TRACE_PUSH
int ret;
switch (i) {
switch (clockIndex) {
<%cases%>
default:
throwStreamPrint(NULL, "Internal Error: unknown sub partition %ld", i);
throwStreamPrint(NULL, "Internal Error: unknown sub partition %ld", clockIndex);
ret = 1;
break;
}
Expand Down
18 changes: 10 additions & 8 deletions OMCompiler/SimulationRuntime/c/simulation/solver/model_help.c
Expand Up @@ -859,27 +859,29 @@ void storeRelations(DATA* data)
TRACE_POP
}

/*! \fn getNextSampleTimeFMU
*
* function return next sample time.
/**
* @brief Get time of next sample event if one is defined.
*
* \param [in] [data]
* Function returns 0 if a time is defined and -1 otherwise.
*
* \author wbraun
* @param data Data
* @param nextSampleEvent On output time of next sample event.
* @return int 1 if a sample event is defined, 0 otherwise
*/
double getNextSampleTimeFMU(DATA *data)
int getNextSampleTimeFMU(DATA *data, double *nextSampleEvent)
{
TRACE_PUSH

if(0 < data->modelData->nSamples)
{
infoStreamPrint(LOG_EVENTS, 0, "Next event time = %f", data->simulationInfo->nextSampleEvent);
TRACE_POP
return data->simulationInfo->nextSampleEvent;
*nextSampleEvent = data->simulationInfo->nextSampleEvent;
return 1 /* TRUE */;
}

TRACE_POP
return -1;
return 0 /* FALSE */;
}

/*! \fn initializeDataStruc
Expand Down
Expand Up @@ -149,7 +149,7 @@ void activateHysteresis(DATA* data);
void storeRelations(DATA* data);
void setZCtol(double relativeTol);

double getNextSampleTimeFMU(DATA *data);
int getNextSampleTimeFMU(DATA *data, double *nextSampleEvent);

void storeOldValues(DATA *data);

Expand Down
91 changes: 71 additions & 20 deletions OMCompiler/SimulationRuntime/c/simulation/solver/synchronous.c
Expand Up @@ -88,7 +88,14 @@ void freeSynchronous(DATA* data)
}


#if !defined(OMC_MINIMAL_RUNTIME)
/**
* @brief Insert given timer into ordered list of timers.
*
* Timer with lowest activation time is at the start of the list, last at the end.
*
* @param list List with timers
* @param timer Timer to insert into list.
*/
static void insertTimer(LIST* list, SYNC_TIMER* timer)
{
TRACE_PUSH
Expand Down Expand Up @@ -138,23 +145,6 @@ void checkForSynchronous(DATA *data, SOLVER_INFO* solverInfo)
TRACE_POP
}

/*
void printSubClock(SUBCLOCK_INFO* subClock)
{
printf("sub-clock\n");
printf("shift: %ld / %ld\n", subClock->shift.m, subClock->shift.n);
printf("factor: %ld / %ld\n", subClock->factor.m, subClock->factor.n);
printf("solverMethod: %s\n", subClock->solverMethod);
printf("holdEvents: %s\n\n", subClock->holdEvents ? "true" : "false");
fflush(stdout);
}
void printRATIONAL(RATIONAL* r)
{
printf("RATIONAL: %ld / %ld\n", r->m, r->n);
fflush(stdout);
}
*/

void fireClock(DATA* data, threadData_t *threadData, long idx, double curTime)
{
Expand Down Expand Up @@ -186,6 +176,7 @@ void fireClock(DATA* data, threadData_t *threadData, long idx, double curTime)
TRACE_POP
}


static void handleBaseClock(DATA* data, threadData_t *threadData, long idx, double curTime)
{
TRACE_PUSH
Expand All @@ -196,7 +187,7 @@ static void handleBaseClock(DATA* data, threadData_t *threadData, long idx, doub
SYNC_TIMER timer;
timer.idx = idx;
timer.type = SYNC_BASE_CLOCK;
timer.activationTime = curTime + clkData->interval;;
timer.activationTime = curTime + clkData->interval;
insertTimer(data->simulationInfo->intvlTimers, &timer);

clkData->timepoint = curTime;
Expand All @@ -205,6 +196,7 @@ static void handleBaseClock(DATA* data, threadData_t *threadData, long idx, doub
TRACE_POP
}

#if !defined(OMC_MINIMAL_RUNTIME)
/**
* Return 0, if there is no fired timers;
1, if there is a fired timer;
Expand Down Expand Up @@ -246,7 +238,66 @@ int handleTimers(DATA* data, threadData_t *threadData, SOLVER_INFO* solverInfo)
TRACE_POP
return ret;
}
#endif /* !defined(OMC_MINIMAL_RUNTIME) */
#endif /* #if !defined(OMC_MINIMAL_RUNTIME) */

/**
* @brief Handle timer clocks and return next time a timer will fire
*
* Update timers and output when the next timer will fire.
* Used for Synchronus features in FMUs.
*
* @param data data
* @param threadData thread data, for errro handling
* @param currentTime Current solver timer.
* @param nextTimerDefined 0 (false) if no next timer is defined.
* 1 (true) if a next timer is defined. Then the time is outputted in nextTimerActivationTime.
* @param nextTimerActivationTime If nextTimerDefined is true it will contain the next time a timer will fire.
* @return int Return 0, if there is no fired timers;
* 1, if there is a fired timer;
* 2, if there is a fired timer which trigger event;
*/
int handleTimersFMI(DATA* data, threadData_t *threadData, double currentTime, int *nextTimerDefined ,double *nextTimerActivationTime)
{
TRACE_PUSH
int ret = 0;
int i=0;

*nextTimerDefined = 0;

if (listLen(data->simulationInfo->intvlTimers) > 0)
{
SYNC_TIMER* nextTimer = (SYNC_TIMER*)listNodeData(listFirstNode(data->simulationInfo->intvlTimers));
while(nextTimer->activationTime <= currentTime + SYNC_EPS)
{
long idx = nextTimer->idx;
double activationTime = nextTimer->activationTime;
SYNC_TIMER_TYPE type = nextTimer->type;
listPopFront(data->simulationInfo->intvlTimers);
switch(type)
{
case SYNC_BASE_CLOCK:
handleBaseClock(data, threadData, idx, activationTime);
break;
case SYNC_SUB_CLOCK:
data->callback->function_equationsSynchronous(data, threadData, idx);
if (data->modelData->subClocksInfo[idx].holdEvents)
ret = 2;
else
ret = ret == 2 ? ret : 1;
break;
}
if (listLen(data->simulationInfo->intvlTimers) == 0) break;
nextTimer = (SYNC_TIMER*)listNodeData(listFirstNode(data->simulationInfo->intvlTimers));
}

/* Next time a timer will activate: */
*nextTimerActivationTime = nextTimer->activationTime;
*nextTimerDefined = 1;
}

TRACE_POP
return ret;
}

#ifdef __cplusplus
}
Expand Down
Expand Up @@ -58,6 +58,7 @@ void freeSynchronous(DATA* data);
void checkForSynchronous(DATA *data, SOLVER_INFO* solverInfo);
void fireClock(DATA* data, threadData_t *threadData, long idx, double curTime);
int handleTimers(DATA *data, threadData_t *threadData, SOLVER_INFO* solverInfo);
int handleTimersFMI(DATA* data, threadData_t *threadData, double currentTime, int *nextTimerDefined ,double *nextTimerActivationTime);


#ifdef __cplusplus
Expand Down
Expand Up @@ -46,6 +46,7 @@
#include "../simulation/solver/fmi_events.h"
#include "../simulation/simulation_info_json.h"
#include "../simulation/simulation_input_xml.h"
#include "../simulation/solver/synchronous.h"
#include "../simulation/options.h"
#include "../util/simulation_options.h"
#include "../util/omc_error.h"
Expand Down Expand Up @@ -84,8 +85,8 @@ const char* stateToString(ModelInstance *comp)
case modelContinuousTimeMode: return "Continuous-Time Mode";
case modelTerminated: return "Terminated";
case modelError: return "Error";
default: return "Unknown";
}
return "Unknown";
}

static fmi2Boolean invalidNumber(ModelInstance *comp, const char *f, const char *arg, int n, int nExpected)
Expand Down Expand Up @@ -215,6 +216,11 @@ fmi2Status fmi2EventUpdate(fmi2Component c, fmi2EventInfo* eventInfo)
int i, done=0;
ModelInstance* comp = (ModelInstance *)c;
threadData_t *threadData = comp->threadData;
fmi2Real nextSampleEvent;
fmi2Boolean nextSampleEventDefined;
fmi2Boolean nextTimerDefined;
fmi2Real nextTimerActivationTime;
int syncRet;

if (nullPointer(comp, "fmi2EventUpdate", "eventInfo", eventInfo)) {
return fmi2Error;
Expand Down Expand Up @@ -259,7 +265,10 @@ fmi2Status fmi2EventUpdate(fmi2Component c, fmi2EventInfo* eventInfo)
}
}

if (checkForDiscreteChanges(comp->fmuData, comp->threadData) || comp->fmuData->simulationInfo->needToIterate || checkRelations(comp->fmuData)) {
/* Handle clock timers */
syncRet = handleTimersFMI(comp->fmuData, comp->threadData, comp->fmuData->localData[0]->timeValue, &nextTimerDefined, &nextTimerActivationTime);

if (checkForDiscreteChanges(comp->fmuData, comp->threadData) || comp->fmuData->simulationInfo->needToIterate || checkRelations(comp->fmuData) || syncRet==2 ) {
FILTERED_LOG(comp, fmi2OK, LOG_FMI2_CALL, "fmi2EventUpdate: Need to iterate(discrete changes)!")
eventInfo->newDiscreteStatesNeeded = fmi2True;
eventInfo->valuesOfContinuousStatesChanged = fmi2True;
Expand All @@ -280,15 +289,24 @@ fmi2Status fmi2EventUpdate(fmi2Component c, fmi2EventInfo* eventInfo)
storePreValues(comp->fmuData);
updateRelationsPre(comp->fmuData);

//Get Next Event Time
double nextSampleEvent=0;
nextSampleEvent = getNextSampleTimeFMU(comp->fmuData);
if (nextSampleEvent == -1) {
eventInfo->nextEventTimeDefined = fmi2False;
} else {
nextSampleEventDefined = getNextSampleTimeFMU(comp->fmuData, &nextSampleEvent);

/* Get next event time */
if (nextSampleEventDefined && !nextTimerDefined) {
eventInfo->nextEventTimeDefined = fmi2True;
eventInfo->nextEventTime = nextSampleEvent;
}
else if (!nextSampleEventDefined && nextTimerDefined) {
eventInfo->nextEventTimeDefined = fmi2True;
eventInfo->nextEventTime = nextTimerActivationTime;
}
else if (nextSampleEventDefined && nextTimerDefined) {
eventInfo->nextEventTimeDefined = fmi2True;
eventInfo->nextEventTime = fmin(nextSampleEvent,nextTimerActivationTime);
}
else {
eventInfo->nextEventTimeDefined = fmi2False;
}
FILTERED_LOG(comp, fmi2OK, LOG_FMI2_CALL, "fmi2EventUpdate: Checked for Sample Events! Next Sample Event %g",eventInfo->nextEventTime)

done=1;
Expand Down Expand Up @@ -514,7 +532,7 @@ fmi2Component fmi2Instantiate(fmi2String instanceName, fmi2Type fmuType, fmi2Str
FILTERED_LOG(comp, fmi2OK, LOG_STATUSWARNING, "fmi2Instantiate: Ignoring unknown resource URI: %s", fmuResourceLocation)
}

/* intialize modelData */
/* initialize modelData */
useStream[LOG_STDOUT] = 1;
useStream[LOG_ASSERT] = 1;
fmu2_model_interface_setupDataStruc(comp->fmuData, comp->threadData);
Expand Down Expand Up @@ -621,6 +639,9 @@ void fmi2FreeInstance(fmi2Component c)
FMI2CS_deInitializeSolverData(comp);
}

/* Free synchronus data */
freeSynchronous(comp->fmuData);

/* free simuation data */
comp->functions->freeMemory(comp->fmuData->modelData);
comp->functions->freeMemory(comp->fmuData->simulationInfo);
Expand Down Expand Up @@ -676,7 +697,8 @@ fmi2Status fmi2ExitInitializationMode(fmi2Component c)
ModelInstance *comp = (ModelInstance *)c;
threadData_t *threadData = comp->threadData;
jmp_buf *old_jmp = threadData->mmc_jumper;
double nextSampleEvent;
fmi2Real nextSampleEvent;
fmi2Boolean nextSampleEventDefined;
int done=0;

threadData->currentErrorStage = ERROR_SIMULATION;
Expand Down Expand Up @@ -715,18 +737,17 @@ fmi2Status fmi2ExitInitializationMode(fmi2Component c)
comp->eventInfo.valuesOfContinuousStatesChanged = fmi2True;

/* get next event time (sample calls) */
nextSampleEvent = 0;
nextSampleEvent = getNextSampleTimeFMU(comp->fmuData);
if (nextSampleEvent == -1)
{
comp->eventInfo.nextEventTimeDefined = fmi2False;
}
else
nextSampleEventDefined = getNextSampleTimeFMU(comp->fmuData, &nextSampleEvent);
if (nextSampleEventDefined)
{
comp->eventInfo.nextEventTimeDefined = fmi2True;
comp->eventInfo.nextEventTime = nextSampleEvent;
fmi2EventUpdate(comp, &(comp->eventInfo));
}
else
{
comp->eventInfo.nextEventTimeDefined = fmi2False;
}
FILTERED_LOG(comp, fmi2OK, LOG_FMI2_CALL, "fmi2EnterInitializationMode: succeed")
res = fmi2OK;

Expand Down

0 comments on commit d66fc7b

Please sign in to comment.