Claudio Gomes 6 vuotta sitten
vanhempi
commit
0b6ef9987f

+ 20 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/dq/modelDescription_me.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<fmiModelDescription
+  fmiVersion="1.0"
+  modelName="dq"
+  modelIdentifier="dq"
+  guid="{8c4e810f-3df3-4a00-8276-176fa3c9f000}"
+  numberOfContinuousStates="1"
+  numberOfEventIndicators="0">
+<ModelVariables>
+  <ScalarVariable name="x" valueReference="0" description="the only state">
+     <Real start="1" fixed="true"/>
+  </ScalarVariable>
+  <ScalarVariable name="der(x)" valueReference="1">
+     <Real/>
+  </ScalarVariable> 
+  <ScalarVariable name="k" valueReference="2" variability="parameter">
+     <Real start="1" fixed="true"/>
+  </ScalarVariable>
+</ModelVariables>
+</fmiModelDescription>

BIN
examples/mass_spring_damper/fmusdk/fmu10/src/models/dq/plot_x.PNG


+ 800 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/fmuTemplate.c

@@ -0,0 +1,800 @@
+/* ---------------------------------------------------------------------------*
+ * Implementation of the FMI interface based on functions and macros to
+ * be defined by the includer of this file. 
+ * If FMI_COSIMULATION is defined, this implements "FMI for Co-Simulation 1.0",
+ * otherwise "FMI for Model Exchange 1.0".
+ * The "FMI for Co-Simulation 1.0", implementation assumes that exactly the 
+ * following capability flags are set to fmiTrue:
+ *    canHandleVariableCommunicationStepSize, i.e. fmiDoStep step size can vary
+ *    canHandleEvents, i.e. fmiDoStep step size can be zero
+ * and all other capability flags are set to default, i.e. to fmiFalse or 0.
+ *
+ * Revision history
+ *  07.02.2010 initial version for "Model Exchange 1.0" released in FMU SDK 1.0
+ *  05.03.2010 bug fix: fmiSetString now copies the passed string argument
+ *     and fmiFreeModelInstance frees all string copies
+ *  11.12.2010 replaced calloc by functions.allocateMemory in fmiInstantiateModel
+ *  04.08.2011 added support for "FMI for Co-Simulation 1.0"
+ *  02.08.2013 fixed a bug in instantiateModel reported by Markus Ende, TH Nuernberg
+ *  02.04.2014 better time event handling
+ *  02.06.2014 copy instanceName and GUID at instantiation
+ *
+ * Copyright QTronic GmbH. All rights reserved.
+ * ---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// array of value references of states
+#if NUMBER_OF_STATES>0
+fmiValueReference vrStates[NUMBER_OF_STATES] = STATES; 
+#endif
+
+#ifndef max
+#define max(a,b) ((a)>(b) ? (a) : (b))
+#endif
+
+#ifndef DT_EVENT_DETECT
+#define DT_EVENT_DETECT 1e-10
+#endif
+
+// ---------------------------------------------------------------------------
+// Private helpers used below to validate function arguments
+// ---------------------------------------------------------------------------
+
+static fmiBoolean invalidNumber(ModelInstance* comp, const char* f, const char* arg, int n, int nExpected){
+    if (n != nExpected) {
+        comp->state = modelError;
+        comp->functions.logger(comp, comp->instanceName, fmiError, "error", 
+                "%s: Invalid argument %s = %d. Expected %d.", f, arg, n, nExpected);
+        return fmiTrue;
+    }
+    return fmiFalse;
+}
+
+static fmiBoolean invalidState(ModelInstance* comp, const char* f, int statesExpected){
+    if (!comp) 
+        return fmiTrue;
+    if (!(comp->state & statesExpected)) {
+        comp->state = modelError;
+        comp->functions.logger(comp, comp->instanceName, fmiError, "error",
+                "%s: Illegal call sequence.", f);
+        return fmiTrue;
+    }
+    return fmiFalse;
+}
+
+static fmiBoolean nullPointer(ModelInstance* comp, const char* f, const char* arg, const void* p){
+    if (!p) {
+        comp->state = modelError;
+        comp->functions.logger(comp, comp->instanceName, fmiError, "error",
+                "%s: Invalid argument %s = NULL.", f, arg);
+        return fmiTrue;
+    }
+    return fmiFalse;
+}
+
+static fmiBoolean vrOutOfRange(ModelInstance* comp, const char* f, fmiValueReference vr, int end) {
+    if (vr >= end) {
+        comp->functions.logger(comp, comp->instanceName, fmiError, "error",
+                "%s: Illegal value reference %u.", f, vr);
+        comp->state = modelError;
+        return fmiTrue;
+    }
+    return fmiFalse;
+}  
+
+// ---------------------------------------------------------------------------
+// Private helpers used below to implement functions
+// ---------------------------------------------------------------------------
+
+fmiStatus setString(fmiComponent comp, fmiValueReference vr, fmiString value){
+    return fmiSetString(comp, &vr, 1, &value);
+}
+
+// fname is fmiInstantiateModel or fmiInstantiateSlave
+static fmiComponent instantiateModel(char* fname, fmiString instanceName, fmiString GUID,
+        fmiCallbackFunctions functions, fmiBoolean loggingOn) {
+    ModelInstance* comp;
+    if (!functions.logger) 
+        return NULL; // we cannot even log this problem
+    if (!instanceName || strlen(instanceName)==0) { 
+        functions.logger(NULL, "?", fmiError, "error",
+                "%s: Missing instance name.", fname);
+        return NULL;
+    }
+    if (!GUID || strlen(GUID)==0) {
+        functions.logger(NULL, instanceName, fmiError, "error",
+                "%s: Missing GUID.", fname);
+        return NULL;
+    }
+    if (!functions.allocateMemory || !functions.freeMemory){
+        functions.logger(NULL, instanceName, fmiError, "error",
+                "%s: Missing callback function.", fname);
+        return NULL;
+    }
+    if (strcmp(GUID, MODEL_GUID)) {
+        functions.logger(NULL, instanceName, fmiError, "error",
+                "%s: Wrong GUID %s. Expected %s.", fname, GUID, MODEL_GUID);
+        return NULL;
+    }
+    comp = (ModelInstance *)functions.allocateMemory(1, sizeof(ModelInstance));
+    if (comp) {
+        comp->r = (fmiReal *)functions.allocateMemory(NUMBER_OF_REALS,    sizeof(fmiReal));
+        comp->i = (fmiInteger *)functions.allocateMemory(NUMBER_OF_INTEGERS, sizeof(fmiInteger));
+        comp->b = (fmiBoolean *)functions.allocateMemory(NUMBER_OF_BOOLEANS, sizeof(fmiBoolean));
+        comp->s = (fmiString *)functions.allocateMemory(NUMBER_OF_STRINGS,  sizeof(fmiString));
+        comp->isPositive = (fmiBoolean *)functions.allocateMemory(NUMBER_OF_EVENT_INDICATORS, sizeof(fmiBoolean));
+        comp->instanceName = (char *)functions.allocateMemory(1 + strlen(instanceName), sizeof(char));
+        comp->GUID = (char *)functions.allocateMemory(1 + strlen(GUID), sizeof(char));
+    }
+    if (!comp || !comp->r || !comp->i || !comp->b || !comp->s || !comp->isPositive
+        || !comp->instanceName || !comp->GUID) {
+        functions.logger(NULL, instanceName, fmiError, "error",
+                "%s: Out of memory.", fname);
+        return NULL;
+    }
+    if (loggingOn) functions.logger(NULL, instanceName, fmiOK, "log",
+            "%s: GUID=%s", fname, GUID);
+
+    strcpy((char *)comp->instanceName, (char *)instanceName);
+    strcpy((char *)comp->GUID, (char *)GUID);
+    comp->functions = functions;
+    comp->loggingOn = loggingOn;
+    comp->state = modelInstantiated;
+    setStartValues(comp); // to be implemented by the includer of this file
+    return comp;
+}
+
+// fname is fmiInitialize or fmiInitializeSlave
+static fmiStatus init(char* fname, fmiComponent c, fmiBoolean toleranceControlled, fmiReal relativeTolerance,
+    fmiEventInfo* eventInfo) {
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, fname, modelInstantiated))
+         return fmiError;
+    if (nullPointer(comp, fname, "eventInfo", eventInfo))
+         return fmiError;
+    if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", 
+        "%s: toleranceControlled=%d relativeTolerance=%g", 
+        fname, toleranceControlled, relativeTolerance);
+    eventInfo->iterationConverged  = fmiTrue;
+    eventInfo->stateValueReferencesChanged = fmiFalse;
+    eventInfo->stateValuesChanged  = fmiFalse;
+    eventInfo->terminateSimulation = fmiFalse;
+    eventInfo->upcomingTimeEvent   = fmiFalse;
+    initialize(comp, eventInfo); // to be implemented by the includer of this file
+    comp->state = modelInitialized;
+    return fmiOK;
+}
+
+// fname is fmiTerminate or fmiTerminateSlave
+static fmiStatus terminate(char* fname, fmiComponent c){
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, fname, modelInitialized))
+         return fmiError;
+    if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", fname);
+    comp->state = modelTerminated;
+    return fmiOK;
+}
+
+// fname is freeModelInstance of freeSlaveInstance
+void freeInstance(char* fname, fmiComponent c) {
+    ModelInstance* comp = (ModelInstance *)c;
+    if (!comp) return;
+    if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", fname);
+    if (comp->r) comp->functions.freeMemory(comp->r);
+    if (comp->i) comp->functions.freeMemory(comp->i);
+    if (comp->b) comp->functions.freeMemory(comp->b);
+    if (comp->s) {
+        int i;
+        for (i=0; i<NUMBER_OF_STRINGS; i++){
+            if (comp->s[i]) comp->functions.freeMemory((void *)comp->s[i]);
+        }
+        comp->functions.freeMemory((void *)comp->s);
+    }
+    if (comp->isPositive) comp->functions.freeMemory(comp->isPositive);
+    if (comp->instanceName) comp->functions.freeMemory((void *)comp->instanceName);
+    if (comp->GUID) comp->functions.freeMemory((void *)comp->GUID);
+    comp->functions.freeMemory(comp);
+}
+
+// ---------------------------------------------------------------------------
+// FMI functions: class methods not depending of a specific model instance
+// ---------------------------------------------------------------------------
+
+const char* fmiGetVersion() {
+    return fmiVersion;
+}
+
+// ---------------------------------------------------------------------------
+// FMI functions: for FMI Model Exchange 1.0 and for FMI Co-Simulation 1.0
+// logging control, setters and getters for Real, Integer, Boolean, String
+// ---------------------------------------------------------------------------
+
+fmiStatus fmiSetDebugLogging(fmiComponent c, fmiBoolean loggingOn) {
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiSetDebugLogging", not_modelError))
+         return fmiError;
+    if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiSetDebugLogging: loggingOn=%d", loggingOn);
+    comp->loggingOn = loggingOn;
+    return fmiOK;
+}
+
+fmiStatus fmiSetReal(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiReal value[]){
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiSetReal", modelInstantiated|modelInitialized))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiSetReal", "vr[]", vr))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiSetReal", "value[]", value))
+         return fmiError;
+    if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiSetReal: nvr = %d", nvr);
+    // no check whether setting the value is allowed in the current state
+    for (i=0; i<nvr; i++) {
+       if (vrOutOfRange(comp, "fmiSetReal", vr[i], NUMBER_OF_REALS))
+           return fmiError;
+       if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiSetReal: #r%d# = %.16g", vr[i], value[i]);
+       comp->r[vr[i]] = value[i];
+    }
+    return fmiOK;
+}
+
+fmiStatus fmiSetInteger(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiInteger value[]){
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiSetInteger", modelInstantiated|modelInitialized))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiSetInteger", "vr[]", vr))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiSetInteger", "value[]", value))
+         return fmiError;
+    if (comp->loggingOn)
+        comp->functions.logger(c, comp->instanceName, fmiOK, "log", "fmiSetInteger: nvr = %d",  nvr);
+    for (i=0; i<nvr; i++) {
+       if (vrOutOfRange(comp, "fmiSetInteger", vr[i], NUMBER_OF_INTEGERS))
+           return fmiError;
+       if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiSetInteger: #i%d# = %d", vr[i], value[i]);
+        comp->i[vr[i]] = value[i]; 
+    }
+    return fmiOK;
+}
+
+fmiStatus fmiSetBoolean(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiBoolean value[]){
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiSetBoolean", modelInstantiated|modelInitialized))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiSetBoolean", "vr[]", vr))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiSetBoolean", "value[]", value))
+         return fmiError;
+    if (comp->loggingOn)
+        comp->functions.logger(c, comp->instanceName, fmiOK, "log", "fmiSetBoolean: nvr = %d",  nvr);
+    for (i=0; i<nvr; i++) {
+        if (vrOutOfRange(comp, "fmiSetBoolean", vr[i], NUMBER_OF_BOOLEANS))
+            return fmiError;
+       if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiSetBoolean: #b%d# = %s", vr[i], value[i] ? "true" : "false");
+        comp->b[vr[i]] = value[i]; 
+    }
+    return fmiOK;
+}
+
+fmiStatus fmiSetString(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiString value[]){
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiSetString", modelInstantiated|modelInitialized))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiSetString", "vr[]", vr))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiSetString", "value[]", value))
+         return fmiError;
+    if (comp->loggingOn)
+        comp->functions.logger(c, comp->instanceName, fmiOK, "log", "fmiSetString: nvr = %d",  nvr);
+    for (i=0; i<nvr; i++) {
+        char *string = (char *)comp->s[vr[i]];
+        if (vrOutOfRange(comp, "fmiSetString", vr[i], NUMBER_OF_STRINGS))
+            return fmiError;
+        if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiSetString: #s%d# = '%s'", vr[i], value[i]);
+        if (value[i] == NULL) {
+            if (string) comp->functions.freeMemory(string);
+            comp->s[vr[i]] = NULL;
+            comp->functions.logger(comp, comp->instanceName, fmiWarning, "warning",
+                            "fmiSetString: string argument value[%d] = NULL.", i);
+        } else {
+            if (string==NULL || strlen(string) < strlen(value[i])) {
+                if (string) comp->functions.freeMemory(string);
+                comp->s[vr[i]] = (char *)comp->functions.allocateMemory(1+strlen(value[i]), sizeof(char));
+                if (!comp->s[vr[i]]) {
+                    comp->state = modelError;
+                    comp->functions.logger(NULL, comp->instanceName, fmiError, "error", "fmiSetString: Out of memory.");
+                    return fmiError;
+                }
+            }
+            strcpy((char *)comp->s[vr[i]], (char *)value[i]);
+        }
+    }
+    return fmiOK;
+}
+
+fmiStatus fmiGetReal(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiReal value[]) {
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiGetReal", not_modelError))
+        return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiGetReal", "vr[]", vr))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiGetReal", "value[]", value))
+         return fmiError;
+#if NUMBER_OF_REALS>0
+    for (i=0; i<nvr; i++) {
+        if (vrOutOfRange(comp, "fmiGetReal", vr[i], NUMBER_OF_REALS))
+            return fmiError;
+        value[i] = getReal(comp, vr[i]); // to be implemented by the includer of this file
+        if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+                "fmiGetReal: #r%u# = %.16g", vr[i], value[i]);
+    }
+#endif
+    return fmiOK;
+}
+
+fmiStatus fmiGetInteger(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiInteger value[]) {
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiGetInteger", not_modelError))
+        return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiGetInteger", "vr[]", vr))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiGetInteger", "value[]", value))
+         return fmiError;
+    for (i=0; i<nvr; i++) {
+        if (vrOutOfRange(comp, "fmiGetInteger", vr[i], NUMBER_OF_INTEGERS))
+           return fmiError;
+        value[i] = comp->i[vr[i]];
+        if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+                "fmiGetInteger: #i%u# = %d", vr[i], value[i]);
+    }
+    return fmiOK;
+}
+
+fmiStatus fmiGetBoolean(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiBoolean value[]) {
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiGetBoolean", not_modelError))
+        return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiGetBoolean", "vr[]", vr))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiGetBoolean", "value[]", value))
+         return fmiError;
+    for (i=0; i<nvr; i++) {
+        if (vrOutOfRange(comp, "fmiGetBoolean", vr[i], NUMBER_OF_BOOLEANS))
+           return fmiError;
+        value[i] = comp->b[vr[i]];
+        if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+                "fmiGetBoolean: #b%u# = %s", vr[i], value[i]? "true" : "false");
+    }
+    return fmiOK;
+}
+
+fmiStatus fmiGetString(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiString  value[]) {
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiGetString", not_modelError))
+        return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiGetString", "vr[]", vr))
+         return fmiError;
+    if (nvr>0 && nullPointer(comp, "fmiGetString", "value[]", value))
+         return fmiError;
+    for (i=0; i<nvr; i++) {
+        if (vrOutOfRange(comp, "fmiGetString", vr[i], NUMBER_OF_STRINGS))
+           return fmiError;
+        value[i] = comp->s[vr[i]];
+        if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+                "fmiGetString: #s%u# = '%s'", vr[i], value[i]);
+    }
+    return fmiOK;
+}
+
+#ifdef FMI_COSIMULATION
+// ---------------------------------------------------------------------------
+// FMI functions: only for FMI Co-Simulation 1.0
+// ---------------------------------------------------------------------------
+
+const char* fmiGetTypesPlatform() {
+    return fmiPlatform;
+}
+
+fmiComponent fmiInstantiateSlave(fmiString  instanceName, fmiString GUID,
+    fmiString fmuLocation, fmiString mimeType, fmiReal timeout, fmiBoolean visible,
+    fmiBoolean interactive, fmiCallbackFunctions functions, fmiBoolean loggingOn) {
+    // ignoring arguments: fmuLocation, mimeType, timeout, visible, interactive
+    return instantiateModel("fmiInstantiateSlave", instanceName, GUID, functions, loggingOn);
+}
+
+fmiStatus fmiInitializeSlave(fmiComponent c, fmiReal tStart, fmiBoolean StopTimeDefined, fmiReal tStop) {
+    ModelInstance* comp = (ModelInstance *)c;
+    fmiBoolean toleranceControlled = fmiFalse;
+    fmiReal relativeTolerance = 0;
+    fmiStatus flag = fmiOK;
+    comp->eventInfo.iterationConverged = 0;
+    while (flag==fmiOK && !comp->eventInfo.iterationConverged) {
+        // ignoring arguments: tStart, StopTimeDefined, tStop
+        flag = init("fmiInitializeSlave", c, toleranceControlled, relativeTolerance, &comp->eventInfo);
+    }
+    return flag;
+}
+
+fmiStatus fmiTerminateSlave(fmiComponent c) {
+    return terminate("fmiTerminateSlave", c);
+}
+
+fmiStatus fmiResetSlave(fmiComponent c) {
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiResetSlave", modelInitialized))
+         return fmiError;
+    if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", "fmiResetSlave");
+    comp->state = modelInstantiated;
+    setStartValues(comp); // to be implemented by the includer of this file
+    return fmiOK;
+}
+
+void fmiFreeSlaveInstance(fmiComponent c) {
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiFreeSlaveInstance", modelTerminated))
+         return;
+    freeInstance("fmiFreeSlaveInstance", c);
+}
+
+fmiStatus fmiSetRealInputDerivatives(fmiComponent c, const fmiValueReference vr[], size_t nvr,
+    const fmiInteger order[], const fmiReal value[]) {
+    ModelInstance* comp = (ModelInstance *)c;
+    fmiCallbackLogger log = comp->functions.logger;
+    if (invalidState(comp, "fmiSetRealInputDerivatives", modelInitialized))
+         return fmiError;
+    if (comp->loggingOn) log(c, comp->instanceName, fmiOK, "log", "fmiSetRealInputDerivatives: nvr= %d", nvr);
+    log(c, comp->instanceName, fmiError, "warning", "fmiSetRealInputDerivatives: ignoring function call."
+      " This model cannot interpolate inputs: canInterpolateInputs=\"fmiFalse\"");
+    return fmiWarning;
+}
+
+fmiStatus fmiGetRealOutputDerivatives(fmiComponent c, const fmiValueReference vr[], size_t nvr,
+    const fmiInteger order[], fmiReal value[]) {
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    fmiCallbackLogger log = comp->functions.logger;
+    if (invalidState(comp, "fmiGetRealOutputDerivatives", modelInitialized))
+         return fmiError;
+    if (comp->loggingOn) log(c, comp->instanceName, fmiOK, "log", "fmiGetRealOutputDerivatives: nvr= %d", nvr);
+    log(c, comp->instanceName, fmiError, "warning", "fmiGetRealOutputDerivatives: ignoring function call."
+      " This model cannot compute derivatives of outputs: MaxOutputDerivativeOrder=\"0\"");
+    for (i=0; i<nvr; i++) value[i] = 0;
+    return fmiWarning;
+}
+
+fmiStatus fmiCancelStep(fmiComponent c) {
+    ModelInstance* comp = (ModelInstance *)c;
+    fmiCallbackLogger log = comp->functions.logger;
+    if (invalidState(comp, "fmiCancelStep", modelInitialized))
+         return fmiError;
+    if (comp->loggingOn) log(c, comp->instanceName, fmiOK, "log", "fmiCancelStep");
+    log(c, comp->instanceName, fmiError, "error", 
+        "fmiCancelStep: Can be called when fmiDoStep returned fmiPending."
+        " This is not the case."); 
+    return fmiError;
+}
+
+fmiStatus fmiDoStep(fmiComponent c, fmiReal currentCommunicationPoint,
+    fmiReal communicationStepSize, fmiBoolean newStep) {
+    ModelInstance* comp = (ModelInstance *)c;
+    fmiCallbackLogger log = comp->functions.logger;
+    double h = communicationStepSize / 10;
+    int k,i;
+    const int n = 10; // how many Euler steps to perform for one do step
+    double prevState[max(NUMBER_OF_STATES, 1)];
+    double prevEventIndicators[max(NUMBER_OF_EVENT_INDICATORS, 1)];
+    int stateEvent = 0;
+
+    if (invalidState(comp, "fmiDoStep", modelInitialized))
+         return fmiError;
+
+    if (comp->loggingOn) log(c, comp->instanceName, fmiOK, "log", "fmiDoStep: "
+       "currentCommunicationPoint = %g, " 
+       "communicationStepSize = %g, " 
+       "newStep = fmi%s",
+       currentCommunicationPoint, communicationStepSize, newStep ? "True" : "False");
+    
+    // Treat also case of zero step, i.e. during an event iteration
+    if (communicationStepSize == 0) {
+        return fmiOK;
+    }
+
+#if NUMBER_OF_EVENT_INDICATORS>0
+    // initialize previous event indicators with current values
+    for (i=0; i<NUMBER_OF_EVENT_INDICATORS; i++) {
+        prevEventIndicators[i] = getEventIndicator(comp, i);
+    }
+#endif
+
+    // break the step into n steps and do forward Euler.
+    comp->time = currentCommunicationPoint;
+    for (k=0; k<n; k++) {
+        comp->time += h;
+
+#if NUMBER_OF_STATES>0
+        for (i=0; i<NUMBER_OF_STATES; i++) {
+            prevState[i] = r(vrStates[i]);
+        }
+        for (i=0; i<NUMBER_OF_STATES; i++) {
+            fmiValueReference vr = vrStates[i];
+            r(vr) += h * getReal(comp, vr+1); // forward Euler step
+        }
+#endif
+
+#if NUMBER_OF_EVENT_INDICATORS>0
+        // check for state event
+        for (i=0; i<NUMBER_OF_EVENT_INDICATORS; i++) {
+            double ei = getEventIndicator(comp, i);
+            if (ei * prevEventIndicators[i] < 0) {
+                if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", 
+                    "fmiDoStep: state event at %g, z%d crosses zero -%c-", comp->time, i, ei<0 ? '\\' : '/');
+                stateEvent++;
+            }
+            prevEventIndicators[i] = ei;
+        }
+        if (stateEvent) {
+            eventUpdate(comp, &comp->eventInfo);
+            stateEvent = 0;
+        } 
+#endif
+        // check for time event
+        if (comp->eventInfo.upcomingTimeEvent && (comp->time - comp->eventInfo.nextEventTime > -DT_EVENT_DETECT)) {
+            if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+                "fmiDoStep: time event detected at %g", comp->time);
+            eventUpdate(comp, &comp->eventInfo);
+        }
+
+        // terminate simulation, if requested by the model
+        if (comp->eventInfo.terminateSimulation) {
+            comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+              "fmiDoStep: model requested termination at t=%g", comp->time);
+            return fmiError; // enforce termination of the simulation loop
+        }
+    }
+    return fmiOK;
+}
+
+static fmiStatus getStatus(char* fname, fmiComponent c, const fmiStatusKind s) {
+    const char* statusKind[3] = {"fmiDoStepStatus","fmiPendingStatus","fmiLastSuccessfulTime"};
+    ModelInstance* comp = (ModelInstance *)c;
+    fmiCallbackLogger log = comp->functions.logger;
+    if (invalidState(comp, fname, modelInstantiated|modelInitialized))
+         return fmiError;
+    if (comp->loggingOn) log(c, comp->instanceName, fmiOK, "log", "$s: fmiStatusKind = %s", fname, statusKind[s]);
+    switch(s) {
+        case fmiDoStepStatus:  log(c, comp->instanceName, fmiError, "error",
+           "%s: Can be called with fmiDoStepStatus when fmiDoStep returned fmiPending."
+           " This is not the case.", fname);
+           break;
+        case fmiPendingStatus:  log(c, comp->instanceName, fmiError, "error", 
+           "%s: Can be called with fmiPendingStatus when fmiDoStep returned fmiPending."
+           " This is not the case.", fname); 
+           break;
+        case fmiLastSuccessfulTime:  log(c, comp->instanceName, fmiError, "error",
+           "%s: Can be called with fmiLastSuccessfulTime when fmiDoStep returned fmiDiscard."
+           " This is not the case.", fname); 
+           break;
+    }
+    return fmiError;
+}
+
+fmiStatus fmiGetStatus(fmiComponent c, const fmiStatusKind s, fmiStatus* value) {
+    return getStatus("fmiGetStatus", c, s);
+}
+
+fmiStatus fmiGetRealStatus(fmiComponent c, const fmiStatusKind s, fmiReal* value){
+    return getStatus("fmiGetRealStatus", c, s);
+}
+
+fmiStatus fmiGetIntegerStatus(fmiComponent c, const fmiStatusKind s, fmiInteger* value){
+    return getStatus("fmiGetIntegerStatus", c, s);
+}
+
+fmiStatus fmiGetBooleanStatus(fmiComponent c, const fmiStatusKind s, fmiBoolean* value){
+    return getStatus("fmiGetBooleanStatus", c, s);
+}
+
+fmiStatus fmiGetStringStatus(fmiComponent c, const fmiStatusKind s, fmiString*  value){
+    return getStatus("fmiGetStringStatus", c, s);
+}
+
+#else
+// ---------------------------------------------------------------------------
+// FMI functions: only for Model Exchange 1.0
+// ---------------------------------------------------------------------------
+
+const char* fmiGetModelTypesPlatform() {
+    return fmiModelTypesPlatform;
+}
+
+fmiComponent fmiInstantiateModel(fmiString instanceName, fmiString GUID, 
+        fmiCallbackFunctions functions, fmiBoolean loggingOn) {
+    return instantiateModel("fmiInstantiateModel", instanceName, GUID, functions, loggingOn);
+}
+
+fmiStatus fmiInitialize(fmiComponent c, fmiBoolean toleranceControlled, fmiReal relativeTolerance,
+    fmiEventInfo* eventInfo) {
+    return init("fmiInitialize", c, toleranceControlled, relativeTolerance, eventInfo);
+}
+
+fmiStatus fmiSetTime(fmiComponent c, fmiReal time) {
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiSetTime", modelInstantiated|modelInitialized))
+         return fmiError;
+    if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiSetTime: time=%.16g", time);
+    comp->time = time;
+    return fmiOK;
+}
+
+fmiStatus fmiSetContinuousStates(fmiComponent c, const fmiReal x[], size_t nx){
+    ModelInstance* comp = (ModelInstance *)c;
+    int i;
+    if (invalidState(comp, "fmiSetContinuousStates", modelInitialized))
+         return fmiError;
+    if (invalidNumber(comp, "fmiSetContinuousStates", "nx", nx, NUMBER_OF_STATES))
+        return fmiError;
+    if (nullPointer(comp, "fmiSetContinuousStates", "x[]", x))
+         return fmiError;
+#if NUMBER_OF_STATES>0
+    for (i=0; i<nx; i++) {
+        fmiValueReference vr = vrStates[i];
+        if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiSetContinuousStates: #r%d#=%.16g", vr, x[i]);
+        assert(vr<NUMBER_OF_REALS);
+        comp->r[vr] = x[i];
+    }
+#endif
+    return fmiOK;
+}
+
+fmiStatus fmiEventUpdate(fmiComponent c, fmiBoolean intermediateResults, fmiEventInfo* eventInfo) {
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiEventUpdate", modelInitialized))
+        return fmiError;
+    if (nullPointer(comp, "fmiEventUpdate", "eventInfo", eventInfo))
+         return fmiError;
+    if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+        "fmiEventUpdate: intermediateResults = %d", intermediateResults);
+    eventInfo->iterationConverged  = fmiTrue;
+    eventInfo->stateValueReferencesChanged = fmiFalse;
+    eventInfo->stateValuesChanged  = fmiFalse;
+    eventInfo->terminateSimulation = fmiFalse;
+    eventInfo->upcomingTimeEvent   = fmiFalse;
+    eventUpdate(comp, eventInfo); // to be implemented by the includer of this file
+    return fmiOK;
+}
+
+fmiStatus fmiCompletedIntegratorStep(fmiComponent c, fmiBoolean* callEventUpdate){
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiCompletedIntegratorStep", modelInitialized))
+         return fmiError;
+    if (nullPointer(comp, "fmiCompletedIntegratorStep", "callEventUpdate", callEventUpdate))
+         return fmiError;
+    if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiCompletedIntegratorStep");
+    *callEventUpdate = fmiFalse;
+    return fmiOK;
+}
+
+fmiStatus fmiGetStateValueReferences(fmiComponent c, fmiValueReference vrx[], size_t nx){
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiGetStateValueReferences", not_modelError))
+        return fmiError;
+    if (invalidNumber(comp, "fmiGetStateValueReferences", "nx", nx, NUMBER_OF_STATES))
+        return fmiError;
+    if (nullPointer(comp, "fmiGetStateValueReferences", "vrx[]", vrx))
+         return fmiError;
+#if NUMBER_OF_STATES>0
+    for (i=0; i<nx; i++) {
+        vrx[i] = vrStates[i];
+        if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiGetStateValueReferences: vrx[%d] = %d", i, vrx[i]);
+    }
+#endif
+    return fmiOK;
+}
+
+fmiStatus fmiGetContinuousStates(fmiComponent c, fmiReal states[], size_t nx){
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiGetContinuousStates", not_modelError))
+        return fmiError;
+    if (invalidNumber(comp, "fmiGetContinuousStates", "nx", nx, NUMBER_OF_STATES))
+        return fmiError;
+    if (nullPointer(comp, "fmiGetContinuousStates", "states[]", states))
+         return fmiError;
+#if NUMBER_OF_STATES>0
+    for (i=0; i<nx; i++) {
+        fmiValueReference vr = vrStates[i];
+        states[i] = getReal(comp, vr); // to be implemented by the includer of this file
+        if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiGetContinuousStates: #r%u# = %.16g", vr, states[i]);
+    }
+#endif
+    return fmiOK;
+}
+
+fmiStatus fmiGetNominalContinuousStates(fmiComponent c, fmiReal x_nominal[], size_t nx){
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiGetNominalContinuousStates", not_modelError))
+        return fmiError;
+    if (invalidNumber(comp, "fmiGetNominalContinuousStates", "nx", nx, NUMBER_OF_STATES))
+        return fmiError;
+    if (nullPointer(comp, "fmiGetNominalContinuousStates", "x_nominal[]", x_nominal))
+         return fmiError;
+    if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+        "fmiGetNominalContinuousStates: x_nominal[0..%d] = 1.0", nx-1);
+    for (i=0; i<nx; i++) 
+        x_nominal[i] = 1;
+    return fmiOK;
+}
+
+fmiStatus fmiGetDerivatives(fmiComponent c, fmiReal derivatives[], size_t nx) {
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiGetDerivatives", not_modelError))
+         return fmiError;
+    if (invalidNumber(comp, "fmiGetDerivatives", "nx", nx, NUMBER_OF_STATES)) 
+        return fmiError;
+    if (nullPointer(comp, "fmiGetDerivatives", "derivatives[]", derivatives))
+         return fmiError;
+#if NUMBER_OF_STATES>0
+    for (i=0; i<nx; i++) {
+        fmiValueReference vr = vrStates[i] + 1;
+        derivatives[i] = getReal(comp, vr); // to be implemented by the includer of this file
+        if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log",
+            "fmiGetDerivatives: #r%d# = %.16g", vr, derivatives[i]);
+    }
+#endif
+    return fmiOK;
+}
+
+fmiStatus fmiGetEventIndicators(fmiComponent c, fmiReal eventIndicators[], size_t ni) {
+    int i;
+    ModelInstance* comp = (ModelInstance *)c;
+    if (invalidState(comp, "fmiGetEventIndicators", not_modelError))
+        return fmiError;
+    if (invalidNumber(comp, "fmiGetEventIndicators", "ni", ni, NUMBER_OF_EVENT_INDICATORS)) 
+        return fmiError;
+#if NUMBER_OF_EVENT_INDICATORS>0
+    for (i=0; i<ni; i++) {
+        eventIndicators[i] = getEventIndicator(comp, i); // to be implemented by the includer of this file
+        if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", 
+            "fmiGetEventIndicators: z%d = %.16g", i, eventIndicators[i]);
+    }
+#endif
+    return fmiOK;
+}
+
+fmiStatus fmiTerminate(fmiComponent c){
+    return terminate("fmiTerminate", c);
+}
+
+void fmiFreeModelInstance(fmiComponent c) {
+    freeInstance("fmiFreeModelInstance", c);
+}
+
+#endif // Model Exchange 1.0
+
+#ifdef __cplusplus
+} // closing brace for extern "C"
+#endif

+ 59 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/fmuTemplate.h

@@ -0,0 +1,59 @@
+/* ---------------------------------------------------------------------------*
+ * fmuTemplate.h
+ * Definitions used in fmiModelFunctions.c and by the includer of this file
+ * Copyright QTronic GmbH. All rights reserved.
+ * ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef FMI_COSIMULATION
+#include "fmiFunctions.h"
+#else
+#include "fmiModelFunctions.h"
+#endif
+
+// macros used to define variables
+#define  r(vr) comp->r[vr]
+#define  i(vr) comp->i[vr]
+#define  b(vr) comp->b[vr]
+#define  s(vr) comp->s[vr]
+#define pos(z) comp->isPositive[z]
+#define copy(vr, value) setString(comp, vr, value)
+
+fmiStatus setString(fmiComponent comp, fmiValueReference vr, fmiString value);
+
+#define not_modelError (modelInstantiated|modelInitialized|modelTerminated)
+
+typedef enum {
+    modelInstantiated = 1<<0,
+    modelInitialized  = 1<<1,
+    modelTerminated   = 1<<2,
+    modelError        = 1<<3
+} ModelState;
+
+typedef struct {
+    fmiReal    *r;
+    fmiInteger *i;
+    fmiBoolean *b;
+    fmiString  *s;
+    fmiBoolean *isPositive;
+    fmiReal time;
+    fmiString instanceName;
+    fmiString GUID;
+    fmiCallbackFunctions functions;
+    fmiBoolean loggingOn;
+    ModelState state;
+#ifdef FMI_COSIMULATION
+    fmiEventInfo eventInfo;
+#endif
+} ModelInstance;
+
+#ifdef __cplusplus
+} // closing brace for extern "C"
+#endif

+ 3 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/inc/Makefile

@@ -0,0 +1,3 @@
+CFLAGS = -I..
+
+include ../Makefile

+ 55 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/inc/inc.c

@@ -0,0 +1,55 @@
+/* ---------------------------------------------------------------------------*
+ * Sample implementation of an FMU - increments an int counter every second.
+ * Copyright QTronic GmbH. All rights reserved.
+ * ---------------------------------------------------------------------------*/
+
+// define class name and unique id
+#define MODEL_IDENTIFIER inc
+#define MODEL_GUID "{8c4e810f-3df3-4a00-8276-176fa3c9f008}"
+
+// define model size
+#define NUMBER_OF_REALS 0
+#define NUMBER_OF_INTEGERS 1
+#define NUMBER_OF_BOOLEANS 0
+#define NUMBER_OF_STRINGS 0
+#define NUMBER_OF_STATES 0
+#define NUMBER_OF_EVENT_INDICATORS 0
+
+// include fmu header files, typedefs and macros
+#include "fmuTemplate.h"
+
+// 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 counter_ 0
+
+// called by fmiInstantiateModel
+// Set values for all variables that define a start value
+// Settings used unless changed by fmiSetX before fmiInitialize
+void setStartValues(ModelInstance *comp) {
+    i(counter_) = 1;
+}
+
+// called by fmiInitialize() after setting eventInfo to defaults
+// Used to set the first time event, if any.
+void initialize(ModelInstance* comp, fmiEventInfo* eventInfo) {
+    eventInfo->upcomingTimeEvent   = fmiTrue;
+    eventInfo->nextEventTime       = 1 + comp->time;
+}
+
+// called by fmiEventUpdate() after setting eventInfo to defaults
+// Used to set the next time event, if any.
+void eventUpdate(ModelInstance* comp, fmiEventInfo* eventInfo) {
+    i(counter_) += 1;
+    if (i(counter_) == 13) 
+        eventInfo->terminateSimulation = fmiTrue;
+    else {
+        eventInfo->upcomingTimeEvent   = fmiTrue;
+        eventInfo->nextEventTime       = 1 + comp->time;
+    }
+} 
+
+// include code that implements the FMI based on the above definitions
+#include "fmuTemplate.c"

+ 22 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/inc/index.html

@@ -0,0 +1,22 @@
+<html>
+<head>
+    <title>Documentation for inc.fmu</title>
+<style type="text/css">
+  html { font-family: Verdana, Arial, Helvetica, sans-serif; }
+  h1   { color: #000066; }
+</style>
+</head>
+<body>
+<h1>inc.fmu</h1>
+This FMU generates time events to increment an integer counter every second and terminates simulation at t=12 sec.
+<br/>    
+<img src="plot_counter.PNG">
+<br/>
+The figure shows the solution computed with Silver.
+
+<h2><a name="license">License conditions</a></h2>
+The FMU is provided by QTronic under the
+<a href="http://www.opensource.org/licenses/bsd-license.html">BSD License</a>.
+</body>
+</html>
+

BIN
examples/mass_spring_damper/fmusdk/fmu10/src/models/inc/model.png


+ 13 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/inc/modelDescription.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<fmiModelDescription
+  fmiVersion="1.0"
+  modelName="inc"
+  modelIdentifier="inc"
+  guid="{8c4e810f-3df3-4a00-8276-176fa3c9f008}"
+  numberOfContinuousStates="0"
+  numberOfEventIndicators="0">
+<ModelVariables>
+  <ScalarVariable name="counter" valueReference="0" description="counts the seconds" causality = "output">
+     <Integer start="1" fixed="true"/>
+  </ScalarVariable>
+</ModelVariables>

+ 21 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/inc/modelDescription_cs.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<fmiModelDescription
+  fmiVersion="1.0"
+  modelName="inc"
+  modelIdentifier="inc"
+  guid="{8c4e810f-3df3-4a00-8276-176fa3c9f008}"
+  numberOfContinuousStates="0"
+  numberOfEventIndicators="0">
+<ModelVariables>
+  <ScalarVariable name="counter" valueReference="0" description="counts the seconds" causality = "output">
+     <Integer start="1" fixed="true"/>
+  </ScalarVariable>
+</ModelVariables>
+<Implementation>
+  <CoSimulation_StandAlone>
+    <Capabilities
+      canHandleVariableCommunicationStepSize="true"
+      canHandleEvents="true"/>
+  </CoSimulation_StandAlone>
+</Implementation>
+</fmiModelDescription>

+ 14 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/inc/modelDescription_me.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<fmiModelDescription
+  fmiVersion="1.0"
+  modelName="inc"
+  modelIdentifier="inc"
+  guid="{8c4e810f-3df3-4a00-8276-176fa3c9f008}"
+  numberOfContinuousStates="0"
+  numberOfEventIndicators="0">
+<ModelVariables>
+  <ScalarVariable name="counter" valueReference="0" description="counts the seconds" causality = "output">
+     <Integer start="1" fixed="true"/>
+  </ScalarVariable>
+</ModelVariables>
+</fmiModelDescription>

BIN
examples/mass_spring_damper/fmusdk/fmu10/src/models/inc/plot_counter.PNG


+ 3 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/values/Makefile

@@ -0,0 +1,3 @@
+CFLAGS = -I..
+
+include ../Makefile

+ 23 - 0
examples/mass_spring_damper/fmusdk/fmu10/src/models/values/index.html

@@ -0,0 +1,23 @@
+<html>
+<head>
+    <title>Documentation for values.fmu</title>
+<style type="text/css">
+  html { font-family: Verdana, Arial, Helvetica, sans-serif; }
+  h1   { color: #000066; }
+</style>
+</head>
+<body>
+    <h1>values.fmu</h1>
+    This FMU demonstrates the use of all four scalar FMU data types 
+    and terminates simulation at t=12 sec.
+    <img src="values.PNG">
+<br>
+The figure shows the solution computed with fmusim using the command 
+<code>fmusim me fmu\me\values.fmu 12 12</code>.
+
+<h2><a name="license">License conditions</a></h2>
+The FMU is provided by QTronic under the
+<a href="http://www.opensource.org/licenses/bsd-license.html">BSD License</a>.
+</body>
+</html>
+