/* Copyright (C) 2012 Modelon AB This program is free software: you can redistribute it and/or modify it under the terms of the BSD style license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the FMILIB_License.txt file for more details. You should have received a copy of the FMILIB_License.txt file along with this program. If not, contact Modelon AB . */ /** \file fmi2_xml_model_structure.c * \brief Implementation for the model structure interface. */ #include #include #include "fmi2_xml_parser.h" #include "fmi2_xml_model_structure_impl.h" #include "fmi2_xml_model_description_impl.h" static const char * module = "FMI2XML"; fmi2_xml_model_structure_t* fmi2_xml_allocate_model_structure(jm_callbacks* cb) { fmi2_xml_model_structure_t* ms = (fmi2_xml_model_structure_t*)(cb->calloc(1, sizeof(fmi2_xml_model_structure_t))); if(!ms) return 0; jm_vector_init(jm_voidp)(&ms->outputs,0,cb); jm_vector_init(jm_voidp)(&ms->derivatives,0,cb); jm_vector_init(jm_voidp)(&ms->discreteStates,0,cb); jm_vector_init(jm_voidp)(&ms->initialUnknowns,0,cb); ms->isValidFlag = 1; ms->outputDeps = fmi2_xml_allocate_dependencies(cb); ms->derivativeDeps = fmi2_xml_allocate_dependencies(cb); ms->discreteStateDeps = fmi2_xml_allocate_dependencies(cb); ms->initialUnknownDeps = fmi2_xml_allocate_dependencies(cb); if(!ms->outputDeps || !ms->derivativeDeps || !ms->discreteStateDeps || !ms->initialUnknownDeps) { fmi2_xml_free_model_structure(ms); return 0; } return ms; } void fmi2_xml_free_model_structure(fmi2_xml_model_structure_t* ms) { jm_callbacks* cb; if(!ms) return; cb = ms->outputs.callbacks; jm_vector_free_data(jm_voidp)(&ms->outputs); jm_vector_free_data(jm_voidp)(&ms->derivatives); jm_vector_free_data(jm_voidp)(&ms->discreteStates); jm_vector_free_data(jm_voidp)(&ms->initialUnknowns); fmi2_xml_free_dependencies(ms->outputDeps); fmi2_xml_free_dependencies(ms->derivativeDeps); fmi2_xml_free_dependencies(ms->discreteStateDeps); fmi2_xml_free_dependencies(ms->initialUnknownDeps); cb->free(ms); } jm_vector(jm_voidp)* fmi2_xml_get_outputs(fmi2_xml_model_structure_t* ms) { return &ms->outputs; } jm_vector(jm_voidp)* fmi2_xml_get_derivatives(fmi2_xml_model_structure_t* ms){ return &ms->derivatives; } jm_vector(jm_voidp)* fmi2_xml_get_discrete_states(fmi2_xml_model_structure_t* ms){ return &ms->discreteStates; } jm_vector(jm_voidp)* fmi2_xml_get_initial_unknowns(fmi2_xml_model_structure_t* ms){ return &ms->initialUnknowns; } void fmi2_xml_get_dependencies(fmi2_xml_dependencies_t* dep, size_t** startIndex, size_t** dependency, char** factorKind){ if(dep) { if (jm_vector_get_size(size_t)(&dep->dependencyIndex) == 0) { *startIndex = NULL; *dependency = NULL; *factorKind = NULL; } else { *startIndex = jm_vector_get_itemp(size_t)(&dep->startIndex, 0); *dependency = jm_vector_get_itemp(size_t)(&dep->dependencyIndex, 0); *factorKind = jm_vector_get_itemp(char)(&dep->dependencyFactorKind, 0); } } else { *startIndex = 0; } } void fmi2_xml_get_outputs_dependencies(fmi2_xml_model_structure_t* ms, size_t** startIndex, size_t** dependency, char** factorKind) { fmi2_xml_get_dependencies(ms->outputDeps, startIndex, dependency, factorKind); } void fmi2_xml_get_derivatives_dependencies(fmi2_xml_model_structure_t* ms, size_t** startIndex, size_t** dependency, char** factorKind) { fmi2_xml_get_dependencies(ms->derivativeDeps, startIndex, dependency, factorKind); } void fmi2_xml_get_discrete_states_dependencies(fmi2_xml_model_structure_t* ms, size_t** startIndex, size_t** dependency, char** factorKind) { fmi2_xml_get_dependencies(ms->discreteStateDeps, startIndex, dependency, factorKind); } void fmi2_xml_get_initial_unknowns_dependencies(fmi2_xml_model_structure_t* ms, size_t** startIndex, size_t** dependency, char** factorKind) { fmi2_xml_get_dependencies(ms->initialUnknownDeps, startIndex, dependency, factorKind); } fmi2_xml_dependencies_t* fmi2_xml_allocate_dependencies(jm_callbacks* cb) { fmi2_xml_dependencies_t* dep = (fmi2_xml_dependencies_t*)(cb->malloc(sizeof(fmi2_xml_dependencies_t))); if(!dep) return 0; jm_vector_init(size_t)(&dep->startIndex, 0, cb); jm_vector_push_back(size_t)(&dep->startIndex, 0); jm_vector_init(size_t)(&dep->dependencyIndex, 0, cb); jm_vector_init(char)(&dep->dependencyFactorKind, 0, cb); dep->isRowMajor = 1; return dep; } void fmi2_xml_zero_empty_dependencies(fmi2_xml_dependencies_t** pdep) { fmi2_xml_dependencies_t* dep =*pdep; size_t ndep = jm_vector_get_size(size_t)(&dep->dependencyIndex); size_t i; if(!dep) return; for(i = 0; idependencyIndex, i)) break; } if(i == ndep) { fmi2_xml_free_dependencies(dep); *pdep = 0; } } void fmi2_xml_free_dependencies(fmi2_xml_dependencies_t* dep) { jm_callbacks* cb; if(!dep) return; cb = dep->startIndex.callbacks; jm_vector_free_data(size_t)(&dep->startIndex); jm_vector_free_data(size_t)(&dep->dependencyIndex); jm_vector_free_data(char)(&dep->dependencyFactorKind); cb->free(dep); } int fmi2_xml_check_model_structure(fmi2_xml_model_description_t* md) { fmi2_xml_model_structure_t* ms = md->modelStructure; if(!ms || !ms->isValidFlag) return 0; return ms->isValidFlag; } int fmi2_xml_handle_ModelStructure(fmi2_xml_parser_context_t *context, const char* data) { fmi2_xml_model_description_t* md = context->modelDescription; if(!data) { jm_log_verbose(context->callbacks, module,"Parsing XML element ModelStructure"); /** allocate model structure */ md->modelStructure = fmi2_xml_allocate_model_structure(md->callbacks); if(!md->modelStructure) { fmi2_xml_parse_fatal(context, module, "Could not allocate memory"); return -1; } } else { /** make sure model structure information is consistent */ if(!fmi2_xml_check_model_structure(md)) { fmi2_xml_parse_fatal(context, "Model structure is not valid due to detected errors. Cannot continue."); return -1; } /* md->numberOfContinuousStates = jm_vector_get_size(jm_voidp)(&md->modelStructure->states); */ } return 0; } int fmi2_xml_handle_Outputs(fmi2_xml_parser_context_t *context, const char* data) { if (!data) { jm_log_verbose(context->callbacks, module, "Parsing XML element Outputs"); /* reset handles for the elements that are specific under Outputs */ /* fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(OutputUnknown));*/ fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(Unknown)); } return 0; } int fmi2_xml_handle_Derivatives(fmi2_xml_parser_context_t *context, const char* data) { if (!data) { jm_log_verbose(context->callbacks, module, "Parsing XML element Derivatives"); /* reset handles for the elements that are specific under Derivatives */ fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(DerivativeUnknown)); } else { fmi2_xml_model_description_t* md = context->modelDescription; fmi2_xml_model_structure_t* ms = md->modelStructure; /* count the number of continuous states as the number of elements under */ md->numberOfContinuousStates = jm_vector_get_size(jm_voidp)(&ms->derivatives); } return 0; } int fmi2_xml_handle_DiscreteStates(fmi2_xml_parser_context_t *context, const char* data) { if (!data) { jm_log_verbose(context->callbacks, module, "Parsing XML element DiscreteStates"); /* reset handles for the elements that are specific under DiscreteStates */ fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(DiscreteStateUnknown)); } return 0; } int fmi2_xml_handle_InitialUnknowns(fmi2_xml_parser_context_t *context, const char* data) { if (!data) { jm_log_verbose(context->callbacks, module, "Parsing XML element InitialUnknowns"); /* reset handles for the elements that are specific under InitialUnknowns */ fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(InitialUnknown)); } return 0; } int fmi2_xml_parse_dependencies(fmi2_xml_parser_context_t *context, fmi2_xml_elm_enu_t parentElmID, fmi2_xml_dependencies_t* deps) { fmi2_xml_model_description_t* md = context->modelDescription; fmi2_xml_model_structure_t* ms = md->modelStructure; const char* listInd; const char* listKind; size_t numDepInd = 0; size_t numDepKind = 0; size_t totNumDep = jm_vector_get_size(size_t)(&deps->dependencyIndex); /* */ if(fmi2_xml_get_attr_str(context, fmi2_xml_elmID_Unknown, fmi_attr_id_dependencies, 0, &listInd)) { ms->isValidFlag = 0; return 0; } if(listInd) { const char* cur = listInd; int ind; while(*cur) { char ch = *cur; while((ch ==' ') || (ch == '\t') || (ch =='\n') || (ch == '\r')) { cur++; ch = *cur; if(!ch) break; } if(!ch) break; if(sscanf(cur, "%d", &ind) != 1) { fmi2_xml_parse_error(context, "XML element 'Unknown': could not parse item %d in the list for attribute 'dependencies'", numDepInd); ms->isValidFlag = 0; return 0; } if(ind < 1) { fmi2_xml_parse_error(context, "XML element 'Unknown': item %d=%d is less than one in the list for attribute 'dependencies'", numDepInd, ind); ms->isValidFlag = 0; return 0; } if(!jm_vector_push_back(size_t)(&deps->dependencyIndex, (size_t)ind)) { fmi2_xml_parse_fatal(context, "Could not allocate memory"); return -1; } while((*cur >= '0') && (*cur <= '9')) cur++; numDepInd++; } } /* */ if(fmi2_xml_get_attr_str(context, fmi2_xml_elmID_Unknown, fmi_attr_id_dependenciesKind, 0, &listKind)) { ms->isValidFlag = 0; return 0; } if(listKind) { const char* cur = listKind; char kind; while(*cur) { char ch = *cur; while(ch && ((ch ==' ') || (ch == '\t') || (ch =='\n') || (ch == '\r'))) { cur++; ch = *cur; } if(!ch) break; if(strncmp("dependent", cur, 9) == 0) { kind = fmi2_dependency_factor_kind_dependent; cur+=9; } else if(strncmp("constant", cur, 8) == 0) { kind = fmi2_dependency_factor_kind_constant; cur+=8; } else if(strncmp("fixed", cur, 5) == 0) { kind = fmi2_dependency_factor_kind_fixed; cur+=5; } else if(strncmp("tunable", cur, 7) == 0) { kind = fmi2_dependency_factor_kind_tunable; cur+=7; } else if(strncmp("discrete", cur, 8) == 0) { kind = fmi2_dependency_factor_kind_discrete; cur+=8; } else { fmi2_xml_parse_error(context, "XML element 'Unknown': could not parse item %d in the list for attribute 'dependenciesKind'", numDepKind); ms->isValidFlag = 0; return 0; } if (parentElmID == fmi2_xml_elmID_InitialUnknowns) { if (kind == fmi2_dependency_factor_kind_fixed) { fmi2_xml_parse_error(context, "XML element 'Unknown' within 'InitialUnknowns': 'fixed' is not allowed in list for attribute 'dependenciesKind'; setting to 'dependent'"); kind = fmi2_dependency_factor_kind_dependent; } else if (!(kind == fmi2_dependency_factor_kind_dependent || kind == fmi2_dependency_factor_kind_constant)) { fmi2_xml_parse_error(context, "XML element 'Unknown' within 'InitialUnknowns': only 'dependent' and 'constant' allowed in list for attribute 'dependenciesKind'"); ms->isValidFlag = 0; return 0; } } if(!jm_vector_push_back(char)(&deps->dependencyFactorKind, kind)) { fmi2_xml_parse_fatal(context, "Could not allocate memory"); return -1; } numDepKind++; } } if(listInd && listKind) { /* both lists are present - the number of items must match */ if(numDepInd != numDepKind) { fmi2_xml_parse_error(context, "XML element 'Unknown': different number of items (%u and %u) in the lists for 'dependencies' and 'dependenciesKind'", numDepInd, numDepKind); ms->isValidFlag = 0; return 0; } } else if(listInd) { /* only Dependencies are present, set all kinds to dependent */ char kind = fmi2_dependency_factor_kind_dependent; if(jm_vector_reserve(char)(&deps->dependencyFactorKind,totNumDep + numDepInd) < totNumDep + numDepInd) { fmi2_xml_parse_fatal(context, "Could not allocate memory"); return -1; } for(;numDepKind < numDepInd; numDepKind++) jm_vector_push_back(char)(&deps->dependencyFactorKind, kind); } else if(listKind) { fmi2_xml_parse_error(context, "XML element 'Unknown': if `dependenciesKind` attribute is present then the `dependencies` attribute must be present also."); ms->isValidFlag = 0; return 0; } else { /* Dependencies are not provided. Put zero index/dependent to indicate that full row must be considered. */ numDepInd = numDepKind = 1; if(!jm_vector_push_back(char)(&deps->dependencyFactorKind, fmi2_dependency_factor_kind_dependent) || !jm_vector_push_back(size_t)(&deps->dependencyIndex, 0) ) { fmi2_xml_parse_fatal(context, "Could not allocate memory"); return -1; } } if(!jm_vector_push_back(size_t)(&deps->startIndex, totNumDep + numDepInd)) { fmi2_xml_parse_fatal(context, "Could not allocate memory"); return -1; } return 0; } int fmi2_xml_parse_unknown(fmi2_xml_parser_context_t *context, fmi2_xml_elm_enu_t parentElmID, jm_vector(jm_voidp) *destVarList, fmi2_xml_dependencies_t* deps) { fmi2_xml_model_description_t* md = context->modelDescription; fmi2_xml_model_structure_t* ms = md->modelStructure; unsigned int index; fmi2_xml_variable_t* variable; /* */ if (fmi2_xml_set_attr_uint(context, fmi2_xml_elmID_Unknown, fmi_attr_id_index, 1, &index, 0)) return -1; index--; /* Convert from one- to zero-based indexing */ /* Ok to just check upper bound since index is unsigned. */ if (index >= jm_vector_get_size(jm_voidp)(md->variablesOrigOrder)) { fmi2_xml_parse_error(context, "The index attribute must have a value between 1 and the number of model variables."); ms->isValidFlag = 0; return -1; } variable = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(md->variablesOrigOrder, index); if (!jm_vector_push_back(jm_voidp)(destVarList, variable)) { fmi2_xml_parse_fatal(context, "Could not allocate memory"); ms->isValidFlag = 0; return -1; } return fmi2_xml_parse_dependencies(context, parentElmID, deps); } /*int fmi2_xml_handle_OutputUnknown(fmi2_xml_parser_context_t *context, const char* data) {*/ int fmi2_xml_handle_Unknown(fmi2_xml_parser_context_t *context, const char* data) { if(!data) { fmi2_xml_model_description_t* md = context->modelDescription; fmi2_xml_model_structure_t* ms = md->modelStructure; return fmi2_xml_parse_unknown(context, fmi2_xml_elmID_Outputs, &ms->outputs, ms->outputDeps); } else { } return 0; } int fmi2_xml_handle_DerivativeUnknown(fmi2_xml_parser_context_t *context, const char* data) { if(!data) { fmi2_xml_model_description_t* md = context->modelDescription; fmi2_xml_model_structure_t* ms = md->modelStructure; int status = fmi2_xml_parse_unknown(context, fmi2_xml_elmID_Derivatives, &ms->derivatives, ms->derivativeDeps); if (status) { return status; } else { fmi2_xml_real_variable_t *der = (fmi2_xml_real_variable_t*) jm_vector_get_last(jm_voidp)(&ms->derivatives); if (!fmi2_xml_get_real_variable_derivative_of(der)) { ms->isValidFlag = 0; fmi2_xml_parse_error(context, "The state derivative '%s' does not specify the state variable that it is a derivative of.", fmi2_xml_get_variable_name((fmi2_xml_variable_t *) der)); return -1; } } } return 0; } int fmi2_xml_handle_DiscreteStateUnknown(fmi2_xml_parser_context_t *context, const char* data) { if(!data) { fmi2_xml_model_description_t* md = context->modelDescription; fmi2_xml_model_structure_t* ms = md->modelStructure; return fmi2_xml_parse_unknown(context, fmi2_xml_elmID_DiscreteStates, &ms->discreteStates, ms->discreteStateDeps); } else { } return 0; } int fmi2_xml_handle_InitialUnknown(fmi2_xml_parser_context_t *context, const char* data) { if(!data) { fmi2_xml_model_description_t* md = context->modelDescription; fmi2_xml_model_structure_t* ms = md->modelStructure; return fmi2_xml_parse_unknown(context, fmi2_xml_elmID_InitialUnknowns, &ms->initialUnknowns, ms->initialUnknownDeps); } else { } return 0; }