Porting dq

Introduction

Here we have a sample implementation of a FMU model. We aim to port it to Nim.

It aims to solve the the Dahlquist test equation.

  • der(x) = - k * x and x(0) = 1.
  • Analytical solution: x(t) = exp(-k*t).

Analysis

The C-code

The file dq.c goes as follows.

Note

MODEL_IDENTIFIER is used by macros in order to create new function names. So it renames: fmiWhatever into dq_fmiWhatever

Then it defines the model size:

#define NUMBER_OF_REALS 3
#define NUMBER_OF_INTEGERS 0
#define NUMBER_OF_BOOLEANS 0
#define NUMBER_OF_STRINGS 0
#define NUMBER_OF_STATES 1
#define NUMBER_OF_EVENT_INDICATORS 0

Note

This values need to be aligned with the .xml file describing the model.

Then:

// include fmu header files, typedefs and macros
#include "fmuTemplate.h"

Then:

// define all model variables and their value references
// conventions used here:
// - if x is a variable, then macro x_ is its variable reference
// - the vr of a variable is its index in array  r, i, b or s
// - if k is the vr of a real state, then k+1 is the vr of its derivative
#define x_     0
#define der_x_ 1
#define k_     2

Then:

// define state vector as vector of value references
#define STATES { x_ }

setStartValues

The function setStartValues set values for all variables that define a start value. Settings used unless changed by fmi2SetX before fmi2EnterInitializationMode. This function is called by fmuTemplate.c > fmuInstantiate:

void setStartValues(ModelInstance *comp) {
    r(x_) = 1;
    r(k_) = 1;
}

calculateValues

The function calculateValues is called when setStartValues or environment set new values through fmi2SetXXX. Lazy set values for all variable that are computed from other variables. It is called by: fmi2GetReal, fmi2GetInteger, fmi2GetBoolean, fmi2GetString, fmi2ExitInitialization.

void calculateValues(ModelInstance *comp) {
    //if (comp->state == modelInitializationMode) {
    //  initialization code here
    //  set first time event, if any, using comp->eventInfo.nextEventTime
    //}
}

getReal

Then:

// called by fmi2GetReal, fmi2GetContinuousStates and fmi2GetDerivatives
fmi2Real getReal(ModelInstance* comp, fmi2ValueReference vr){
    switch (vr) {
        case x_     : return r(x_);
        case der_x_ : return - r(k_) * r(x_);
        case k_     : return r(k_);
        default: return 0;
    }
}

eventUpdate

Then:

// used to set the next time event, if any.
void eventUpdate(ModelInstance *comp, fmi2EventInfo *eventInfo, int isTimeEvent, int isNewEventIteration) {
} 

fmuTemplate.c

Finally, include code that implements the FMI based on the above definitions:

#include "fmuTemplate.c"