diff --git a/include/fmi4c.h b/include/fmi4c.h index 5f8ab2e..a2caa61 100644 --- a/include/fmi4c.h +++ b/include/fmi4c.h @@ -412,6 +412,67 @@ FMIC_DLLEXPORT void fmi3GetClockType(fmiHandle *fmu, FMIC_DLLEXPORT int fmi3GetNumberOfLogCategories(fmiHandle *fmu); FMIC_DLLEXPORT void fmi3GetLogCategory(fmiHandle *fmu, int id, const char **name, const char **description); +FMIC_DLLEXPORT int fmi3GetNumberOfModelStructureOutputs(fmiHandle *fmu); +FMIC_DLLEXPORT void fmi3GetModelStructureOutput(fmiHandle *fmu, + int id, + fmi3ValueReference *vr, + int *numberOfDependencies, + bool *dependencyKindsDefined); +FMIC_DLLEXPORT fmi3ValueReference fmi3GetModelStructureOutputDependency(fmiHandle *fmu, + int outputId, + int dependencyId); +FMIC_DLLEXPORT fmi3ValueReference fmi3GetModelStructureOutputDependencyKind(fmiHandle *fmu, + int outputId, + int dependencyId); +FMIC_DLLEXPORT int fmi3GetNumberOfModelStructureContinuousStateDerivatives(fmiHandle *fmu); +FMIC_DLLEXPORT void fmi3GetModelStructureContinuousStateDerivative(fmiHandle *fmu, + int id, + fmi3ValueReference *vr, + int *numberOfDependencies, + bool *dependencyKindsDefined); +FMIC_DLLEXPORT fmi3ValueReference fmi3GetModelStructureContinuousStateDerivativeDependency(fmiHandle *fmu, + int derId, + int dependencyId); +FMIC_DLLEXPORT fmi3ValueReference fmi3GetModelStructureContinuousStateDerivativeDependencyKind(fmiHandle *fmu, + int derId, + int dependencyId); +FMIC_DLLEXPORT int fmi3GetNumberOfModelStructureClockedStates(fmiHandle *fmu); +FMIC_DLLEXPORT void fmi3GetModelStructureClockedState(fmiHandle *fmu, + int id, + fmi3ValueReference *vr, + int *numberOfDependencies, + bool *dependencyKindsDefined); +FMIC_DLLEXPORT fmi3ValueReference fmi3GetModelStructureClockedStateDependency(fmiHandle *fmu, + int clockId, + int dependencyId); +FMIC_DLLEXPORT fmi3ValueReference fmi3GetModelStructureClockedStateDependencyKind(fmiHandle *fmu, + int clockId, + int dependencyId); +FMIC_DLLEXPORT int fmi3GetNumberOfModelStructureInitialUnknowns(fmiHandle *fmu); +FMIC_DLLEXPORT void fmi3GetModelStructureInitialUnknown(fmiHandle *fmu, + int id, + fmi3ValueReference *vr, + int *numberOfDependencies, + bool *dependencyKindsDefined); +FMIC_DLLEXPORT fmi3ValueReference fmi3GetModelStructureInitialUnknownDependency(fmiHandle *fmu, + int unknownId, + int dependencyId); +FMIC_DLLEXPORT fmi3ValueReference fmi3ModelStructureGetInitialUnknownDependencyKind(fmiHandle *fmu, + int unknownId, + int dependencyId); +FMIC_DLLEXPORT int fmi3GetNumberOfModelStructureEventIndicators(fmiHandle *fmu); +FMIC_DLLEXPORT void fmi3GetModelStructureEventIndicator(fmiHandle *fmu, + int id, + fmi3ValueReference *vr, + int *numberOfDependencies, + bool *dependencyKindsDefined); +FMIC_DLLEXPORT fmi3ValueReference fmi3GetModelStructureEventIndicatorDependency(fmiHandle *fmu, + int indicatorId, + int dependencyId); +FMIC_DLLEXPORT fmi3ValueReference fmi3GetModelStructureEventIndicatorDependencyKind(fmiHandle *fmu, + int indicatorId, + int dependencyId); + FMIC_DLLEXPORT const char* fmi3GetModelIdentifier(fmiHandle* fmu); FMIC_DLLEXPORT bool fmi3GetNeedsExecutionTool(fmiHandle* fmu); FMIC_DLLEXPORT bool fmi3GetCanBeInstantiatedOnlyOncePerProcess(fmiHandle* fmu); diff --git a/include/fmi4c_private.h b/include/fmi4c_private.h index 71ba222..e730b3c 100644 --- a/include/fmi4c_private.h +++ b/include/fmi4c_private.h @@ -491,6 +491,14 @@ typedef struct { const char* description; } fmi3LogCategory; +typedef struct { + fmi3ValueReference valueReference; + int numberOfDependencies; + bool dependencyKindsDefined; + fmi3ValueReference *dependencies; + fmi3DependencyKind *dependencyKinds; +} fmi3ModelStructureElement; + typedef struct { bool supportsModelExchange; bool supportsCoSimulation; @@ -674,6 +682,17 @@ typedef struct { int numberOfLogCategories; fmi3LogCategory *logCategories; + + int numberOfOutputs; + int numberOfContinuousStateDerivatives; + int numberOfClockedStates; + int numberOfInitialUnknowns; + int numberOfEventIndicators; + fmi3ModelStructureElement *outputs; + fmi3ModelStructureElement *continuousStateDerivatives; + fmi3ModelStructureElement *clockedStates; + fmi3ModelStructureElement *initialUnknowns; + fmi3ModelStructureElement *eventIndicators; } fmi3Data_t; diff --git a/include/fmi4c_utils.h b/include/fmi4c_utils.h index 79e5544..3874c78 100644 --- a/include/fmi4c_utils.h +++ b/include/fmi4c_utils.h @@ -4,6 +4,7 @@ #include #include #include "3rdparty/ezxml/ezxml.h" +#include "fmi4c_private.h" const char* getFunctionName(const char* modelName, const char* functionName); @@ -20,4 +21,6 @@ bool parseUInt32AttributeEzXml(ezxml_t element, const char* attributeName, uint3 bool parseUInt16AttributeEzXml(ezxml_t element, const char *attributeName, uint16_t* target); bool parseUInt8AttributeEzXml(ezxml_t element, const char *attributeName, uint8_t *target); +bool parseModelStructureElement(fmi3ModelStructureElement *output, ezxml_t *element); + #endif // FMIC_UTILS_H diff --git a/src/fmi4c.c b/src/fmi4c.c index dc020a1..3db92b4 100644 --- a/src/fmi4c.c +++ b/src/fmi4c.c @@ -212,8 +212,6 @@ bool parseModelDescriptionFmi1(fmiHandle *fmu) } } - ezxml_free(rootElement); - chdir(cwd); return true; @@ -1300,6 +1298,89 @@ bool parseModelDescriptionFmi3(fmiHandle *fmu) } } + ezxml_t modelStructureElement = ezxml_child(rootElement, "ModelStructure"); + fmu->fmi3.numberOfOutputs = 0; + if(modelStructureElement) { + //Count each element type + ezxml_t outputElement = ezxml_child(modelStructureElement, "Output"); + for(;outputElement;outputElement = outputElement->next) { + ++fmu->fmi3.numberOfOutputs; + } + ezxml_t continuousStateDerElement = ezxml_child(modelStructureElement, "ContinuousStateDerivative"); + for(;continuousStateDerElement;continuousStateDerElement = continuousStateDerElement->next) { + ++fmu->fmi3.numberOfContinuousStateDerivatives; + } + ezxml_t clockedStateElement = ezxml_child(modelStructureElement, "ClockedState"); + for(;clockedStateElement;clockedStateElement = clockedStateElement->next) { + ++fmu->fmi3.numberOfClockedStates; + } + ezxml_t initialUnknownElement = ezxml_child(modelStructureElement, "InitialUnknown"); + for(;initialUnknownElement;initialUnknownElement = initialUnknownElement->next) { + ++fmu->fmi3.numberOfInitialUnknowns; + } + ezxml_t eventIndicatorElement = ezxml_child(modelStructureElement, "EventIndicator"); + for(;eventIndicatorElement;eventIndicatorElement = eventIndicatorElement->next) { + ++fmu->fmi3.numberOfEventIndicators; + } + + //Allocate memory for each element type + fmu->fmi3.outputs = malloc(fmu->fmi3.numberOfOutputs*sizeof(fmi3ModelStructureElement)); + fmu->fmi3.continuousStateDerivatives = malloc(fmu->fmi3.numberOfContinuousStateDerivatives*sizeof(fmi3ModelStructureElement)); + fmu->fmi3.clockedStates = malloc(fmu->fmi3.numberOfClockedStates*sizeof(fmi3ModelStructureElement)); + fmu->fmi3.initialUnknowns = malloc(fmu->fmi3.numberOfInitialUnknowns*sizeof(fmi3ModelStructureElement)); + fmu->fmi3.eventIndicators = malloc(fmu->fmi3.numberOfEventIndicators*sizeof(fmi3ModelStructureElement)); + + //Read outputs + int i=0; + outputElement = ezxml_child(modelStructureElement, "Output"); + for(;outputElement;outputElement = outputElement->next) { + if(!parseModelStructureElement(&fmu->fmi3.outputs[i], &outputElement)) { + return false; + } + ++i; + } + + //Read continuous state derivatives + i=0; + continuousStateDerElement = ezxml_child(modelStructureElement, "ContinuousStateDerivative"); + for(;continuousStateDerElement;continuousStateDerElement = continuousStateDerElement->next) { + if(!parseModelStructureElement(&fmu->fmi3.continuousStateDerivatives[i], &continuousStateDerElement)) { + return false; + } + ++i; + } + + //Read clocked states + i=0; + clockedStateElement = ezxml_child(modelStructureElement, "ClockedState"); + for(;clockedStateElement;clockedStateElement = clockedStateElement->next) { + if(!parseModelStructureElement(&fmu->fmi3.clockedStates[i], &clockedStateElement)) { + return false; + } + ++i; + } + + //Read initial unknowns + i=0; + initialUnknownElement = ezxml_child(modelStructureElement, "IninitalUnknown"); + for(;initialUnknownElement;initialUnknownElement = initialUnknownElement->next) { + if(!parseModelStructureElement(&fmu->fmi3.initialUnknowns[i], &initialUnknownElement)) { + return false; + } + ++i; + } + + //Read event indicators + i=0; + eventIndicatorElement = ezxml_child(modelStructureElement, "EventIndicator"); + for(;eventIndicatorElement;eventIndicatorElement = eventIndicatorElement->next) { + if(!parseModelStructureElement(&fmu->fmi3.eventIndicators[i], &eventIndicatorElement)) { + return false; + } + ++i; + } + } + ezxml_free(rootElement); chdir(cwd); @@ -4528,3 +4609,194 @@ void fmi3GetLogCategory(fmiHandle *fmu, int id, const char **name, const char ** *description = fmu->fmi3.logCategories[id].description; } } + +int fmi3GetNumberOfModelStructureOutputs(fmiHandle *fmu) +{ + return fmu->fmi3.numberOfOutputs; +} + +void fmi3GetModelStructureOutput(fmiHandle *fmu, + int id, + fmi3ValueReference *vr, + int *numberOfDependencies, + bool *dependencyKindsDefined) +{ + if(id < fmu->fmi3.numberOfOutputs) { + *vr = fmu->fmi3.outputs[id].valueReference; + *numberOfDependencies = fmu->fmi3.outputs[id].numberOfDependencies; + *dependencyKindsDefined = fmu->fmi3.outputs[id].dependencyKindsDefined; + } +} + +fmi3ValueReference fmi3GetModelStructureOutputDependency(fmiHandle *fmu, int outputId, int dependencyId) +{ + if(outputId < fmu->fmi3.numberOfOutputs && + dependencyId < fmu->fmi3.outputs[outputId].numberOfDependencies) { + return fmu->fmi3.outputs[outputId].dependencies[dependencyId]; + } +} + +fmi3ValueReference fmi3GetModelStructureOutputDependencyKind(fmiHandle *fmu, int outputId, int dependencyId) +{ + if(outputId < fmu->fmi3.numberOfOutputs && + dependencyId < fmu->fmi3.outputs[outputId].numberOfDependencies && + fmu->fmi3.outputs[outputId].dependencyKindsDefined) { + return fmu->fmi3.outputs[outputId].dependencyKinds[dependencyId]; + } +} + +int fmi3GetNumberOfModelStructureContinuousStateDerivatives(fmiHandle *fmu) +{ + return fmu->fmi3.numberOfContinuousStateDerivatives; +} + +void fmi3GetModelStructureContinuousStateDerivative(fmiHandle *fmu, + int id, + fmi3ValueReference *vr, + int *numberOfDependencies, + bool *dependencyKindsDefined) +{ + if(id < fmu->fmi3.numberOfContinuousStateDerivatives) { + *vr = fmu->fmi3.continuousStateDerivatives[id].valueReference; + *numberOfDependencies = fmu->fmi3.continuousStateDerivatives[id].numberOfDependencies; + *dependencyKindsDefined = fmu->fmi3.continuousStateDerivatives[id].dependencyKindsDefined; + } +} + +fmi3ValueReference fmi3GetModelStructureContinuousStateDerivativeDependency(fmiHandle *fmu, + int derId, + int dependencyId) +{ + if(derId < fmu->fmi3.numberOfContinuousStateDerivatives && + dependencyId < fmu->fmi3.continuousStateDerivatives[derId].numberOfDependencies) { + return fmu->fmi3.continuousStateDerivatives[derId].dependencies[dependencyId]; + } +} + +fmi3ValueReference fmi3GetModelStructureContinuousStateDerivativeDependencyKind(fmiHandle *fmu, + int derId, + int dependencyId) +{ + if(derId < fmu->fmi3.numberOfContinuousStateDerivatives && + dependencyId < fmu->fmi3.continuousStateDerivatives[derId].numberOfDependencies && + fmu->fmi3.continuousStateDerivatives[derId].dependencyKindsDefined) { + return fmu->fmi3.continuousStateDerivatives[derId].dependencyKinds[dependencyId]; + } +} + +int fmi3GetNumberOfModelStructureClockedStates(fmiHandle *fmu) +{ + return fmu->fmi3.numberOfClockedStates; +} + +void fmi3GetModelStructureClockedState(fmiHandle *fmu, + int id, + fmi3ValueReference *vr, + int *numberOfDependencies, + bool *dependencyKindsDefined) +{ + if(id < fmu->fmi3.numberOfClockedStates) { + *vr = fmu->fmi3.clockedStates[id].valueReference; + *numberOfDependencies = fmu->fmi3.clockedStates[id].numberOfDependencies; + *dependencyKindsDefined = fmu->fmi3.clockedStates[id].dependencyKindsDefined; + } +} + +fmi3ValueReference fmi3GetModelStructureClockedStateDependency(fmiHandle *fmu, + int clockId, + int dependencyId) +{ + if(clockId < fmu->fmi3.numberOfClockedStates && + dependencyId < fmu->fmi3.clockedStates[clockId].numberOfDependencies) { + return fmu->fmi3.clockedStates[clockId].dependencies[dependencyId]; + } +} + +fmi3ValueReference fmi3GetModelStructureClockedStateDependencyKind(fmiHandle *fmu, + int clockId, + int dependencyId) +{ + if(clockId < fmu->fmi3.numberOfClockedStates && + dependencyId < fmu->fmi3.clockedStates[clockId].numberOfDependencies && + fmu->fmi3.clockedStates[clockId].dependencyKindsDefined) { + return fmu->fmi3.clockedStates[clockId].dependencyKinds[dependencyId]; + } +} + +int fmi3GetNumberOfModelStructureInitialUnknowns(fmiHandle *fmu) +{ + return fmu->fmi3.numberOfInitialUnknowns; +} + +void fmi3GetModelStructureInitialUnknown(fmiHandle *fmu, + int id, + fmi3ValueReference *vr, + int *numberOfDependencies, + bool *dependencyKindsDefined) +{ + if(id < fmu->fmi3.numberOfInitialUnknowns) { + *vr = fmu->fmi3.initialUnknowns[id].valueReference; + *numberOfDependencies = fmu->fmi3.initialUnknowns[id].numberOfDependencies; + *dependencyKindsDefined = fmu->fmi3.initialUnknowns[id].dependencyKindsDefined; + } +} + +fmi3ValueReference fmi3GetModelStructureInitialUnknownDependency(fmiHandle *fmu, + int unknownId, + int dependencyId) +{ + if(unknownId < fmu->fmi3.numberOfInitialUnknowns && + dependencyId < fmu->fmi3.initialUnknowns[unknownId].numberOfDependencies) { + return fmu->fmi3.initialUnknowns[unknownId].dependencies[dependencyId]; + } +} + +fmi3ValueReference fmi3ModelStructureGetInitialUnknownDependencyKind(fmiHandle *fmu, + int unknownId, + int dependencyId) +{ + if(unknownId < fmu->fmi3.numberOfInitialUnknowns && + dependencyId < fmu->fmi3.initialUnknowns[unknownId].numberOfDependencies && + fmu->fmi3.initialUnknowns[unknownId].dependencyKindsDefined) { + return fmu->fmi3.initialUnknowns[unknownId].dependencyKinds[dependencyId]; + } +} + +int fmi3GetNumberOfModelStructureEventIndicators(fmiHandle *fmu) +{ + return fmu->fmi3.numberOfEventIndicators; +} + +void fmi3GetModelStructureEventIndicator(fmiHandle *fmu, + int id, + fmi3ValueReference *vr, + int *numberOfDependencies, + bool *dependencyKindsDefined) +{ + if(id < fmu->fmi3.numberOfEventIndicators) { + *vr = fmu->fmi3.eventIndicators[id].valueReference; + *numberOfDependencies = fmu->fmi3.eventIndicators[id].numberOfDependencies; + *dependencyKindsDefined = fmu->fmi3.eventIndicators[id].dependencyKindsDefined; + } +} + +fmi3ValueReference fmi3GetModelStructureEventIndicatorDependency(fmiHandle *fmu, + int indicatorId, + int dependencyId) +{ + if(indicatorId < fmu->fmi3.numberOfEventIndicators && + dependencyId < fmu->fmi3.eventIndicators[indicatorId].numberOfDependencies) { + return fmu->fmi3.eventIndicators[indicatorId].dependencies[dependencyId]; + } +} + +fmi3ValueReference fmi3GetModelStructureEventIndicatorDependencyKind(fmiHandle *fmu, + int indicatorId, + int dependencyId) +{ + if(indicatorId < fmu->fmi3.numberOfEventIndicators && + dependencyId < fmu->fmi3.eventIndicators[indicatorId].numberOfDependencies && + fmu->fmi3.eventIndicators[indicatorId].dependencyKindsDefined) { + return fmu->fmi3.eventIndicators[indicatorId].dependencyKinds[dependencyId]; + } +} diff --git a/src/fmi4c_utils.c b/src/fmi4c_utils.c index 9bec992..2c141fc 100644 --- a/src/fmi4c_utils.c +++ b/src/fmi4c_utils.c @@ -185,3 +185,86 @@ bool parseUInt8AttributeEzXml(ezxml_t element, const char *attributeName, uint8_ } return false; } + +bool parseModelStructureElement(fmi3ModelStructureElement *output, ezxml_t *element) +{ + output->valueReference = 42; + parseUInt32AttributeEzXml(*element, "valueReference", &output->valueReference); + + //Count number of dependencies + const char* dependencies = ""; + parseStringAttributeEzXml(*element, "dependencies", &dependencies); + char* nonConstDependencies = strdup(dependencies); + + //Count number of dependencies + output->numberOfDependencies = 0; + if(nonConstDependencies[0]) { + output->numberOfDependencies = 1; + } + for(int i=0; nonConstDependencies[i]; ++i) { + if(nonConstDependencies[i] == ' ') { + ++output->numberOfDependencies; + } + } + + //Allocate memory for dependencies + output->dependencies = malloc(output->numberOfDependencies*sizeof(int)); + + //Read dependencies + const char* delim = " "; + for(int j=0; jnumberOfDependencies; ++j) { + if(j == 0) { + output->dependencies[j] = atoi(strtok(nonConstDependencies, delim)); + } + else { + output->dependencies[j] = atoi(strtok(NULL, delim)); + } + } + + //Parse depenendency kinds element if present + const char* dependencyKinds = NULL; + parseStringAttributeEzXml(*element, "dependencyKinds", &dependencyKinds); + if(dependencyKinds) { + char* nonConstDependencyKinds = strdup(dependencyKinds); + + //Allocate memory for dependencies (assume same number as dependencies, according to FMI3 specification) + output->dependencies = malloc(output->numberOfDependencies*sizeof(int)); + + //Read dependency kinds + const char* delim = " "; + for(int j=0; jnumberOfDependencies; ++j) { + const char* kind; + if(j == 0) { + kind = strtok(nonConstDependencyKinds, delim); + } + else { + kind = strtok(NULL, delim); + } + + if(!strcmp(kind, "independent")) { + fmi4cErrorMessage = strdup("Dependency kind = \"independent\" is not allowed for output dependencies."); + return false; + } + else if(!strcmp(kind, "constant")) { + output->dependencyKinds[j] = fmi3Constant; + } + else if(!strcmp(kind, "fixed")) { + output->dependencyKinds[j] = fmi3Fixed; + } + else if(!strcmp(kind, "tunable")) { + output->dependencyKinds[j] = fmi3Tunable; + } + else if(!strcmp(kind, "discrete")) { + output->dependencyKinds[j] = fmi3Discrete; + } + else if(!strcmp(kind, "dependent")) { + output->dependencyKinds[j] = fmi3Dependent; + } + else { + fmi4cErrorMessage = strdup("Unknown dependency kind for output dependency."); + return false; + } + } + } + return true; +}