]> gerrit.simantics Code Review - simantics/fmil.git/blob - org.simantics.fmil.core/native/FMILibrary/src/XML/src/FMI2/fmi2_xml_model_structure.c
Add FMILibrary-2.0.3 to org.simantics.fmil.core\native.
[simantics/fmil.git] / org.simantics.fmil.core / native / FMILibrary / src / XML / src / FMI2 / fmi2_xml_model_structure.c
1 /*
2     Copyright (C) 2012 Modelon AB
3
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the BSD style license.
6
7      This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10     FMILIB_License.txt file for more details.
11
12     You should have received a copy of the FMILIB_License.txt file
13     along with this program. If not, contact Modelon AB <http://www.modelon.com>.
14 */
15
16 /** \file fmi2_xml_model_structure.c
17 *  \brief Implementation for the model structure interface.
18 */
19 #include <string.h>
20 #include <stdio.h>
21
22 #include "fmi2_xml_parser.h"
23 #include "fmi2_xml_model_structure_impl.h"
24 #include "fmi2_xml_model_description_impl.h"
25
26 static const char * module = "FMI2XML";
27
28 fmi2_xml_model_structure_t* fmi2_xml_allocate_model_structure(jm_callbacks* cb) {
29         fmi2_xml_model_structure_t* ms = (fmi2_xml_model_structure_t*)(cb->calloc(1, sizeof(fmi2_xml_model_structure_t)));
30         if(!ms) return 0;
31
32         jm_vector_init(jm_voidp)(&ms->outputs,0,cb);
33         jm_vector_init(jm_voidp)(&ms->derivatives,0,cb);
34         jm_vector_init(jm_voidp)(&ms->discreteStates,0,cb);
35         jm_vector_init(jm_voidp)(&ms->initialUnknowns,0,cb);
36
37         ms->isValidFlag = 1;
38
39     ms->outputDeps = fmi2_xml_allocate_dependencies(cb);
40     ms->derivativeDeps = fmi2_xml_allocate_dependencies(cb);
41     ms->discreteStateDeps = fmi2_xml_allocate_dependencies(cb);
42     ms->initialUnknownDeps = fmi2_xml_allocate_dependencies(cb);
43
44         if(!ms->outputDeps || !ms->derivativeDeps || !ms->discreteStateDeps || !ms->initialUnknownDeps) {
45                 fmi2_xml_free_model_structure(ms);
46                 return 0;
47         }
48
49         return ms;
50 }
51
52 void fmi2_xml_free_model_structure(fmi2_xml_model_structure_t* ms) {
53         jm_callbacks* cb;
54         if(!ms) return;
55         cb = ms->outputs.callbacks;
56
57         jm_vector_free_data(jm_voidp)(&ms->outputs);
58         jm_vector_free_data(jm_voidp)(&ms->derivatives);
59         jm_vector_free_data(jm_voidp)(&ms->discreteStates);
60         jm_vector_free_data(jm_voidp)(&ms->initialUnknowns);
61
62     fmi2_xml_free_dependencies(ms->outputDeps);
63     fmi2_xml_free_dependencies(ms->derivativeDeps);
64     fmi2_xml_free_dependencies(ms->discreteStateDeps);
65     fmi2_xml_free_dependencies(ms->initialUnknownDeps);
66         cb->free(ms);
67 }
68
69 jm_vector(jm_voidp)* fmi2_xml_get_outputs(fmi2_xml_model_structure_t* ms) {
70         return &ms->outputs;
71 }
72
73 jm_vector(jm_voidp)* fmi2_xml_get_derivatives(fmi2_xml_model_structure_t* ms){
74         return &ms->derivatives;
75 }
76
77 jm_vector(jm_voidp)* fmi2_xml_get_discrete_states(fmi2_xml_model_structure_t* ms){
78         return &ms->discreteStates;
79 }
80
81 jm_vector(jm_voidp)* fmi2_xml_get_initial_unknowns(fmi2_xml_model_structure_t* ms){
82         return &ms->initialUnknowns;
83 }
84
85
86 void fmi2_xml_get_dependencies(fmi2_xml_dependencies_t* dep, size_t** startIndex, size_t** dependency, char** factorKind){
87     if(dep) {
88         if (jm_vector_get_size(size_t)(&dep->dependencyIndex) == 0) {
89             *startIndex = NULL;
90             *dependency = NULL;
91             *factorKind = NULL;
92         } else {
93             *startIndex = jm_vector_get_itemp(size_t)(&dep->startIndex, 0);
94             *dependency = jm_vector_get_itemp(size_t)(&dep->dependencyIndex, 0);
95             *factorKind = jm_vector_get_itemp(char)(&dep->dependencyFactorKind, 0);
96         }
97     }
98     else {
99         *startIndex = 0;
100     }
101 }
102
103 void fmi2_xml_get_outputs_dependencies(fmi2_xml_model_structure_t* ms,
104                                            size_t** startIndex, size_t** dependency, char** factorKind) {
105     fmi2_xml_get_dependencies(ms->outputDeps, startIndex, dependency, factorKind);
106 }
107
108 void fmi2_xml_get_derivatives_dependencies(fmi2_xml_model_structure_t* ms,
109                                            size_t** startIndex, size_t** dependency, char** factorKind) {
110     fmi2_xml_get_dependencies(ms->derivativeDeps, startIndex, dependency, factorKind);
111 }
112
113 void fmi2_xml_get_discrete_states_dependencies(fmi2_xml_model_structure_t* ms,
114                                            size_t** startIndex, size_t** dependency, char** factorKind) {
115     fmi2_xml_get_dependencies(ms->discreteStateDeps, startIndex, dependency, factorKind);
116 }
117
118 void fmi2_xml_get_initial_unknowns_dependencies(fmi2_xml_model_structure_t* ms,
119                                            size_t** startIndex, size_t** dependency, char** factorKind) {
120     fmi2_xml_get_dependencies(ms->initialUnknownDeps, startIndex, dependency, factorKind);
121 }
122
123
124 fmi2_xml_dependencies_t* fmi2_xml_allocate_dependencies(jm_callbacks* cb) {
125         fmi2_xml_dependencies_t* dep = (fmi2_xml_dependencies_t*)(cb->malloc(sizeof(fmi2_xml_dependencies_t)));
126         if(!dep) return 0;
127         jm_vector_init(size_t)(&dep->startIndex, 0, cb);
128         jm_vector_push_back(size_t)(&dep->startIndex, 0);
129
130         jm_vector_init(size_t)(&dep->dependencyIndex, 0, cb);
131         jm_vector_init(char)(&dep->dependencyFactorKind, 0, cb);
132
133         dep->isRowMajor = 1;
134
135         return dep;
136 }
137
138 void fmi2_xml_zero_empty_dependencies(fmi2_xml_dependencies_t** pdep) {
139         fmi2_xml_dependencies_t* dep =*pdep;
140         size_t ndep = jm_vector_get_size(size_t)(&dep->dependencyIndex);
141         size_t i;
142         if(!dep) return;
143         for(i = 0; i<ndep;i++) {
144                 if(jm_vector_get_item(size_t)(&dep->dependencyIndex, i)) break;
145         }
146         if(i == ndep) {
147                 fmi2_xml_free_dependencies(dep);
148                 *pdep = 0;
149         }
150 }
151
152
153 void fmi2_xml_free_dependencies(fmi2_xml_dependencies_t* dep) {
154     jm_callbacks* cb;
155         if(!dep) return;
156     cb = dep->startIndex.callbacks;
157         jm_vector_free_data(size_t)(&dep->startIndex);
158
159         jm_vector_free_data(size_t)(&dep->dependencyIndex);
160         jm_vector_free_data(char)(&dep->dependencyFactorKind);
161     cb->free(dep);
162 }
163
164
165 int fmi2_xml_check_model_structure(fmi2_xml_model_description_t* md) {
166         fmi2_xml_model_structure_t* ms = md->modelStructure;
167
168         if(!ms || !ms->isValidFlag) return 0;
169
170         return ms->isValidFlag;
171 }
172
173 int fmi2_xml_handle_ModelStructure(fmi2_xml_parser_context_t *context, const char* data) {
174     fmi2_xml_model_description_t* md = context->modelDescription;
175     if(!data) {
176                 jm_log_verbose(context->callbacks, module,"Parsing XML element ModelStructure");
177                 /** allocate model structure */
178                 md->modelStructure = fmi2_xml_allocate_model_structure(md->callbacks);
179                 if(!md->modelStructure) {
180                                 fmi2_xml_parse_fatal(context, module, "Could not allocate memory");
181                                 return -1;
182                 }
183     }
184     else {
185                 /** make sure model structure information is consistent */
186
187                 if(!fmi2_xml_check_model_structure(md)) {
188                         fmi2_xml_parse_fatal(context, "Model structure is not valid due to detected errors. Cannot continue.");
189                         return -1;
190                 }
191 /*              md->numberOfContinuousStates = jm_vector_get_size(jm_voidp)(&md->modelStructure->states); */
192
193     }
194     return 0;
195 }
196
197
198 int fmi2_xml_handle_Outputs(fmi2_xml_parser_context_t *context, const char* data) {
199     if (!data) {
200         jm_log_verbose(context->callbacks, module, "Parsing XML element Outputs");
201         /*  reset handles for the elements that are specific under Outputs */
202 /*        fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(OutputUnknown));*/
203         fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(Unknown));
204     }
205     return 0;
206 }
207 int fmi2_xml_handle_Derivatives(fmi2_xml_parser_context_t *context, const char* data) {
208     if (!data) {
209         jm_log_verbose(context->callbacks, module, "Parsing XML element Derivatives");
210         /*  reset handles for the elements that are specific under Derivatives */
211         fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(DerivativeUnknown));
212     }
213     else {
214         fmi2_xml_model_description_t* md = context->modelDescription;
215         fmi2_xml_model_structure_t* ms = md->modelStructure;
216         /* count the number of continuous states as the number of <Unknown> elements under <Derivatives> */
217         md->numberOfContinuousStates = jm_vector_get_size(jm_voidp)(&ms->derivatives);
218     }
219     return 0;
220 }
221 int fmi2_xml_handle_DiscreteStates(fmi2_xml_parser_context_t *context, const char* data) {
222     if (!data) {
223         jm_log_verbose(context->callbacks, module, "Parsing XML element DiscreteStates");
224         /*  reset handles for the elements that are specific under DiscreteStates */
225         fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(DiscreteStateUnknown));
226     }
227     return 0;
228 }
229 int fmi2_xml_handle_InitialUnknowns(fmi2_xml_parser_context_t *context, const char* data) {
230     if (!data) {
231         jm_log_verbose(context->callbacks, module, "Parsing XML element InitialUnknowns");
232         /*  reset handles for the elements that are specific under InitialUnknowns */
233         fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(InitialUnknown));
234     }
235     return 0;
236 }
237
238
239 int fmi2_xml_parse_dependencies(fmi2_xml_parser_context_t *context,
240                                 fmi2_xml_elm_enu_t parentElmID,
241                                                                 fmi2_xml_dependencies_t* deps)
242 {
243     fmi2_xml_model_description_t* md = context->modelDescription;
244     fmi2_xml_model_structure_t* ms = md->modelStructure;
245
246     const char* listInd;
247     const char* listKind;
248     size_t numDepInd = 0;
249     size_t numDepKind = 0;
250     size_t totNumDep = jm_vector_get_size(size_t)(&deps->dependencyIndex);
251
252     /*  <xs:attribute name="dependencies">
253             <xs:simpleType>
254                 <xs:list itemType="xs:unsignedInt"/>
255             </xs:simpleType>
256         </xs:attribute> */
257     if(fmi2_xml_get_attr_str(context, fmi2_xml_elmID_Unknown, fmi_attr_id_dependencies, 0, &listInd)) {
258         ms->isValidFlag = 0;
259         return 0;
260     }
261     if(listInd) {
262          const char* cur = listInd;
263          int ind;
264          while(*cur) {
265              char ch = *cur;
266              while((ch ==' ') || (ch == '\t') || (ch =='\n') || (ch == '\r')) {
267                  cur++; ch = *cur;
268                  if(!ch) break;
269              }
270              if(!ch) break;
271              if(sscanf(cur, "%d", &ind) != 1) {
272                  fmi2_xml_parse_error(context, "XML element 'Unknown': could not parse item %d in the list for attribute 'dependencies'",
273                      numDepInd);
274                 ms->isValidFlag = 0;
275                 return 0;
276              }
277              if(ind < 1) {
278                  fmi2_xml_parse_error(context, "XML element 'Unknown': item %d=%d is less than one in the list for attribute 'dependencies'",
279                      numDepInd, ind);
280                 ms->isValidFlag = 0;
281                 return 0;
282              }
283              if(!jm_vector_push_back(size_t)(&deps->dependencyIndex, (size_t)ind)) {
284                 fmi2_xml_parse_fatal(context, "Could not allocate memory");
285                 return -1;
286             }
287              while((*cur >= '0') && (*cur <= '9')) cur++;
288              numDepInd++;
289          }
290     }
291
292     /*
293         <xs:attribute name="dependenciesKind">
294             <xs:simpleType>
295                 <xs:list>
296                     <xs:simpleType>
297                         <xs:restriction base="xs:normalizedString">
298                             <xs:enumeration value="dependent"/>
299                             <xs:enumeration value="constant"/>
300                             <xs:enumeration value="fixed"/>
301                             <xs:enumeration value="tunable"/>
302                             <xs:enumeration value="discrete"/>
303                         </xs:restriction>
304                     </xs:simpleType>
305                 </xs:list>
306             </xs:simpleType>
307         </xs:attribute>
308         */
309     if(fmi2_xml_get_attr_str(context, fmi2_xml_elmID_Unknown, fmi_attr_id_dependenciesKind, 0, &listKind)) {
310         ms->isValidFlag = 0;
311         return 0;
312     }
313     if(listKind) {
314          const char* cur = listKind;
315          char kind;
316          while(*cur) {
317              char ch = *cur;
318              while(ch && ((ch ==' ') || (ch == '\t') || (ch =='\n') || (ch == '\r'))) {
319                  cur++; ch = *cur;
320              }
321              if(!ch) break;
322              if(strncmp("dependent", cur, 9) == 0) {
323                  kind = fmi2_dependency_factor_kind_dependent;
324                  cur+=9;
325              }
326              else if(strncmp("constant", cur, 8) == 0) {
327                  kind = fmi2_dependency_factor_kind_constant;
328                  cur+=8;
329              }
330              else if(strncmp("fixed", cur, 5) == 0) {
331                  kind = fmi2_dependency_factor_kind_fixed;
332                  cur+=5;
333              }
334              else if(strncmp("tunable", cur, 7) == 0) {
335                  kind = fmi2_dependency_factor_kind_tunable;
336                  cur+=7;
337              }
338              else if(strncmp("discrete", cur, 8) == 0) {
339                   kind = fmi2_dependency_factor_kind_discrete;
340                  cur+=8;
341              }
342              else {
343                  fmi2_xml_parse_error(context, "XML element 'Unknown': could not parse item %d in the list for attribute 'dependenciesKind'",
344                      numDepKind);
345                  ms->isValidFlag = 0;
346                  return 0;
347              }
348              if (parentElmID == fmi2_xml_elmID_InitialUnknowns) {
349                 if (kind == fmi2_dependency_factor_kind_fixed) {
350                     fmi2_xml_parse_error(context, "XML element 'Unknown' within 'InitialUnknowns': 'fixed' is not allowed in list for attribute 'dependenciesKind'; setting to 'dependent'");
351                     kind = fmi2_dependency_factor_kind_dependent;
352                 }
353                 else if (!(kind == fmi2_dependency_factor_kind_dependent || kind == fmi2_dependency_factor_kind_constant)) {
354                     fmi2_xml_parse_error(context, "XML element 'Unknown' within 'InitialUnknowns': only 'dependent' and 'constant' allowed in list for attribute 'dependenciesKind'");
355                     ms->isValidFlag = 0;
356                     return 0;
357                 }
358              }
359              if(!jm_vector_push_back(char)(&deps->dependencyFactorKind, kind)) {
360                 fmi2_xml_parse_fatal(context, "Could not allocate memory");
361                 return -1;
362             }
363              numDepKind++;
364          }
365     }
366     if(listInd && listKind) {
367         /* both lists are present - the number of items must match */
368         if(numDepInd != numDepKind) {
369             fmi2_xml_parse_error(context, "XML element 'Unknown': different number of items (%u and %u) in the lists for 'dependencies' and 'dependenciesKind'",
370                                  numDepInd, numDepKind);
371             ms->isValidFlag = 0;
372             return 0;
373         }
374     }
375     else if(listInd) {
376         /* only Dependencies are present, set all kinds to dependent */
377         char kind = fmi2_dependency_factor_kind_dependent;
378         if(jm_vector_reserve(char)(&deps->dependencyFactorKind,totNumDep + numDepInd) < totNumDep + numDepInd) {
379             fmi2_xml_parse_fatal(context, "Could not allocate memory");
380             return -1;
381         }
382         for(;numDepKind < numDepInd; numDepKind++)
383             jm_vector_push_back(char)(&deps->dependencyFactorKind, kind);
384     }
385     else if(listKind) {
386         fmi2_xml_parse_error(context, "XML element 'Unknown': if `dependenciesKind` attribute is present then the `dependencies` attribute must be present also.");
387         ms->isValidFlag = 0;
388         return 0;
389     }
390     else {
391         /* Dependencies are not provided. Put zero index/dependent to indicate that full row must be considered. */
392         numDepInd = numDepKind = 1;
393         if(!jm_vector_push_back(char)(&deps->dependencyFactorKind, fmi2_dependency_factor_kind_dependent) ||
394          !jm_vector_push_back(size_t)(&deps->dependencyIndex, 0)
395           ) {
396             fmi2_xml_parse_fatal(context, "Could not allocate memory");
397             return -1;
398         }
399     }
400     if(!jm_vector_push_back(size_t)(&deps->startIndex, totNumDep + numDepInd)) {
401         fmi2_xml_parse_fatal(context, "Could not allocate memory");
402         return -1;
403     }
404
405     return 0;
406 }
407
408
409 int fmi2_xml_parse_unknown(fmi2_xml_parser_context_t *context,
410                            fmi2_xml_elm_enu_t parentElmID,
411                            jm_vector(jm_voidp) *destVarList,
412                            fmi2_xml_dependencies_t* deps)
413 {
414     fmi2_xml_model_description_t* md = context->modelDescription;
415     fmi2_xml_model_structure_t* ms = md->modelStructure;
416
417     unsigned int index;
418     fmi2_xml_variable_t* variable;
419
420     /* <xs:attribute name="index" type="xs:unsignedInt" use="required"> */
421     if (fmi2_xml_set_attr_uint(context, fmi2_xml_elmID_Unknown, fmi_attr_id_index, 1, &index, 0)) return -1;
422     index--; /* Convert from one- to zero-based indexing */
423
424     /* Ok to just check upper bound since index is unsigned. */
425     if (index >= jm_vector_get_size(jm_voidp)(md->variablesOrigOrder)) {
426         fmi2_xml_parse_error(context, "The index attribute must have a value between 1 and the number of model variables.");
427         ms->isValidFlag = 0;
428         return -1;
429     }
430     variable = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(md->variablesOrigOrder, index);
431
432     if (!jm_vector_push_back(jm_voidp)(destVarList, variable)) {
433         fmi2_xml_parse_fatal(context, "Could not allocate memory");
434         ms->isValidFlag = 0;
435         return -1;
436     }
437
438     return fmi2_xml_parse_dependencies(context, parentElmID, deps);
439 }
440
441
442 /*int fmi2_xml_handle_OutputUnknown(fmi2_xml_parser_context_t *context, const char* data) {*/
443 int fmi2_xml_handle_Unknown(fmi2_xml_parser_context_t *context, const char* data) {
444     if(!data) {
445         fmi2_xml_model_description_t* md = context->modelDescription;
446         fmi2_xml_model_structure_t* ms = md->modelStructure;
447
448         return fmi2_xml_parse_unknown(context, fmi2_xml_elmID_Outputs, &ms->outputs, ms->outputDeps);
449     }
450     else {
451     }
452     return 0;
453 }
454
455 int fmi2_xml_handle_DerivativeUnknown(fmi2_xml_parser_context_t *context, const char* data) {
456     if(!data) {
457         fmi2_xml_model_description_t* md = context->modelDescription;
458         fmi2_xml_model_structure_t* ms = md->modelStructure;
459         int status = fmi2_xml_parse_unknown(context, fmi2_xml_elmID_Derivatives, &ms->derivatives, ms->derivativeDeps);
460
461         if (status) {
462             return status;
463         } else {
464             fmi2_xml_real_variable_t *der = (fmi2_xml_real_variable_t*) jm_vector_get_last(jm_voidp)(&ms->derivatives);
465             if (!fmi2_xml_get_real_variable_derivative_of(der)) {
466                 ms->isValidFlag = 0;
467                 fmi2_xml_parse_error(context,
468                         "The state derivative '%s' does not specify the state variable that it is a derivative of.",
469                         fmi2_xml_get_variable_name((fmi2_xml_variable_t *) der));
470                 return -1;
471             }
472         }
473     }
474     return 0;
475 }
476
477 int fmi2_xml_handle_DiscreteStateUnknown(fmi2_xml_parser_context_t *context, const char* data) {
478     if(!data) {
479         fmi2_xml_model_description_t* md = context->modelDescription;
480         fmi2_xml_model_structure_t* ms = md->modelStructure;
481
482         return fmi2_xml_parse_unknown(context, fmi2_xml_elmID_DiscreteStates, &ms->discreteStates, ms->discreteStateDeps);
483     }
484     else {
485     }
486     return 0;
487 }
488
489 int fmi2_xml_handle_InitialUnknown(fmi2_xml_parser_context_t *context, const char* data) {
490     if(!data) {
491         fmi2_xml_model_description_t* md = context->modelDescription;
492         fmi2_xml_model_structure_t* ms = md->modelStructure;
493
494         return fmi2_xml_parse_unknown(context, fmi2_xml_elmID_InitialUnknowns, &ms->initialUnknowns, ms->initialUnknownDeps);
495     }
496     else {
497     }
498     return 0;
499 }