Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 76.2% 571 / 0 / 749
Functions: -% 0 / 1 / 1
Branches: 66.3% 428 / 0 / 646

OMCompiler/Compiler/SimCode/SimCodeCodegenUtil.mo
Line Branch Exec Source
1 /*
2 * This file is part of OpenModelica.
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC),
5 * c/o Linköpings universitet, Department of Computer and Information Science,
6 * SE-58183 Linköping, Sweden.
7 *
8 * All rights reserved.
9 *
10 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR
11 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8.
12 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
13 * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL
14 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
15 *
16 * The OpenModelica software and the OSMC (Open Source Modelica Consortium)
17 * Public License (OSMC-PL) are obtained from OSMC, either from the above
18 * address, from the URLs:
19 * http://www.openmodelica.org or
20 * https://github.com/OpenModelica/ or
21 * http://www.ida.liu.se/projects/OpenModelica,
22 * and in the OpenModelica distribution.
23 *
24 * GNU AGPL version 3 is obtained from:
25 * https://www.gnu.org/licenses/licenses.html#GPL
26 *
27 * This program is distributed WITHOUT ANY WARRANTY; without
28 * even the implied warranty of MERCHANTABILITY or FITNESS
29 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
30 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
31 *
32 * See the full OSMC Public License conditions for more details.
33 *
34 */
35
36 encapsulated package SimCodeCodegenUtil
37 "SimCode queries the code generators need: the variable, value-reference and
38 equation lookups they call while emitting code."
39
40 import Absyn;
41 import BackendDAE;
42 import DAE;
43 import DoubleEnded;
44 import ExpressionBasics;
45 import HashTable;
46 import HashTableCrIListArray;
47 import HashTableCrILst;
48 import SimCode;
49 import SimCodeFunction;
50 import SimCodeVar;
51 import Types;
52 import UnorderedSet;
53 import Util;
54
55 protected
56 import AbsynUtil;
57 import Array;
58 import AvlTreeCRToInt;
59 import BaseHashTable;
60 import ClassInf;
61 import ComponentReference;
62 import ComponentReferenceBasics;
63 import Config;
64 import DAEDump;
65 import MetaModelica.Dangerous;
66 import Error;
67 import Expression;
68 import ExpressionDump;
69 import Flags;
70 import Global;
71 import HashTableCrefSimVar;
72 import List;
73 import SimCodeFunctionUtil;
74 import SimCodeUtilShared;
75 import StringUtil;
76 import System;
77 import UnitAbsyn;
78 import UnitAbsynBuilder;
79 import UnitParserExt;
80 import UnorderedMap;
81
82 public
83
84 protected function compareEqSystems
85 input SimCode.SimEqSystem eq1;
86 input SimCode.SimEqSystem eq2;
87 output Boolean b;
88 algorithm
89 1792846 b := simEqSystemIndex(eq1) > simEqSystemIndex(eq2);
90 end compareEqSystems;
91
92 public function sortEqSystems
93 input list<SimCode.SimEqSystem> eqs;
94 output list<SimCode.SimEqSystem> outEqs;
95 algorithm
96
4/4
✓ Branch 0 taken 378175 times.
✓ Branch 1 taken 20372 times.
✓ Branch 2 taken 378175 times.
✓ Branch 3 taken 20372 times.
398547 outEqs := List.flatten(list(expandEntwined(eq) for eq in eqs));
97 20372 outEqs := List.sort(outEqs, compareEqSystems);
98 end sortEqSystems;
99
100 protected function expandEntwined
101 "expands entwined equations to their body equation systems
102 used for serializing"
103 input SimCode.SimEqSystem eq;
104 output list<SimCode.SimEqSystem> eqs;
105 algorithm
106 eqs := match eq
107 2 case SimCode.SES_ENTWINED_ASSIGN() then eq :: eq.single_calls;
108 else {eq};
109 end match;
110 end expandEntwined;
111
112 public function getClockIndex "author: rfranke
113 Returns the index of the clock of a variable or zero non-clocked variables"
114 input SimCodeVar.SimVar simVar;
115 input SimCode.SimCode simCode;
116 output Option<Integer> clockIndex;
117 protected
118 DAE.ComponentRef cref;
119 HashTable.HashTable clkHT;
120 algorithm
121 4572 cref := getSimVarCompRef(simVar);
122 clockIndex := match simCode
123
2/2
✓ Branch 1 taken 92 times.
✓ Branch 2 taken 4480 times.
4572 case SimCode.SIMCODE(crefToClockIndexHT=clkHT) then
124 if BaseHashTable.hasKey(cref, clkHT)
125 then SOME(BaseHashTable.get(cref, clkHT))
126 else NONE();
127 end match;
128 end getClockIndex;
129
130 public function getSimVarCompRef
131 input SimCodeVar.SimVar inVar;
132 output DAE.ComponentRef outComp;
133 algorithm
134 5121 outComp := inVar.name;
135 end getSimVarCompRef;
136
137 public function getSubPartitions
138 input list<SimCode.ClockedPartition> inPartitions;
139 output list<SimCode.SubPartition> outSubPartitions;
140 algorithm
141 3103 outSubPartitions := List.flatten(List.map(inPartitions, getSubPartition));
142 end getSubPartitions;
143
144 public function getSubPartition
145 input SimCode.ClockedPartition inPartition;
146 output list<SimCode.SubPartition> outSubPartitions;
147 algorithm
148 266 outSubPartitions := inPartition.subPartitions;
149 end getSubPartition;
150
151 public function getClockedEquations
152 input list<SimCode.SubPartition> inSubPartitions;
153 output list<SimCode.SimEqSystem> outEqs = {};
154 algorithm
155
2/2
✓ Branch 0 taken 254 times.
✓ Branch 1 taken 1654 times.
1908 for part in inSubPartitions loop
156 254 outEqs := listAppend(part.equations, outEqs);
157 254 outEqs := listAppend(part.removedEquations, outEqs);
158 end for;
159 end getClockedEquations;
160
161 public function jacobianColumnsAreEmpty
162 input list<SimCode.JacobianColumn> columns;
163 output Boolean b = true;
164 algorithm
165
2/2
✓ Branch 0 taken 2100 times.
✓ Branch 1 taken 590 times.
2690 for col in columns loop
166
3/4
✓ Branch 0 taken 590 times.
✓ Branch 1 taken 1510 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 590 times.
2100 if not (listEmpty(col.columnEqns) and listEmpty(col.constantEqns)) then
167 b := false;
168 1510 return;
169 end if;
170 end for;
171 end jacobianColumnsAreEmpty;
172
173 public function stripAsubIfNoIter
174 "Strips a RELATION's optionExpisASUB (see the comment on DAE.RELATION, and
175 NBEvents.mo's asubTuple) whenever the caller has no regenerated for-loop of its
176 own around this expression (hasIter = false). optionExpisASUB names the iterator
177 cref a state-event condition was originally wrapped in, so CodegenCFunctions.tpl's
178 zero-crossing template can offset storedRelations[] per iteration -- but that only
179 compiles when the SAME for-loop is regenerated at the call site, giving the
180 iterator an actual in-scope C variable. When the caller has already fully unrolled
181 this expression into an independent scalar occurrence (hasIter = false), the
182 RELATION's own index is already correct standalone, and the stored iterator cref
183 has no corresponding loop variable to reference: codegen falls back to emitting
184 its bare (often source-level, e.g. \"i\") name, which doesn't compile (see
185 PNlib.Test2.mos and the other tests this fixes in CodegenC.tpl's zeroCrossingTpl/
186 relationTpl, the only current callers)."
187 input DAE.Exp exp;
188 input Boolean hasIter;
189 output DAE.Exp outExp;
190 algorithm
191
2/2
✓ Branch 0 taken 6485 times.
✓ Branch 1 taken 22 times.
6507 outExp := if hasIter then exp else match exp
192 case DAE.RELATION(optionExpisASUB = SOME(_))
193 53 then DAE.RELATION(exp.exp1, exp.operator, exp.exp2, exp.index, NONE());
194 case DAE.LBINARY()
195 698 then DAE.LBINARY(stripAsubIfNoIter(exp.exp1, hasIter), exp.operator, stripAsubIfNoIter(exp.exp2, hasIter));
196 case DAE.LUNARY()
197 89 then DAE.LUNARY(exp.operator, stripAsubIfNoIter(exp.exp, hasIter));
198 else exp;
199 end match;
200 end stripAsubIfNoIter;
201
202 public function dimsToAllIndexes
203 input DAE.Dimensions inDims;
204 output list<list<Integer>> outIndexes;
205 protected
206 list<Integer> ilst;
207 list<list<Integer>> lstlst;
208 algorithm
209 ✗ ilst := Expression.dimensionsSizes(inDims);
210 ✗ lstlst := List.map(ilst, List.intRange);
211 ✗ outIndexes := dimsToAllIndexes1(lstlst);
212 end dimsToAllIndexes;
213
214 protected function dimsToAllIndexes1
215 input list<list<Integer>> inDims;
216 output list<list<Integer>> oAllIndex;
217 algorithm
218 oAllIndex := match inDims
219 local
220 list<Integer> dims;
221 list<list<Integer>> rest, indxes;
222 case dims::{}
223 algorithm
224 ✗ indxes := List.map(dims, List.create);
225 then
226 indxes;
227 case dims::rest
228 algorithm
229 ✗ indxes := dimsToAllIndexes1(rest);
230 // cons for each element in dims
231 ✗ indxes := List.fold1(dims, dimsToAllIndexes2, indxes, {});
232 then
233 indxes;
234 end match;
235 end dimsToAllIndexes1;
236
237 protected function dimsToAllIndexes2
238 input Integer i;
239 input list<list<Integer>> iIndex;
240 input list<list<Integer>> iAllIndex;
241 output list<list<Integer>> oAllIndex;
242 algorithm
243 ✗ oAllIndex := List.map1(iIndex, List.consr, i);
244 ✗ oAllIndex := listAppend(iAllIndex, oAllIndex);
245 end dimsToAllIndexes2;
246
247 public function getDefaultFmiInitialAttribute
248 "Get the defualt fmi 2.0 initial attribute."
249 input SimCodeVar.Variability variability;
250 input SimCodeVar.Causality causality;
251 output SimCodeVar.Initial initial_;
252 algorithm
253 initial_ := match(variability, causality)
254 // CONSTANT
255 case (SimCodeVar.CONSTANT(), SimCodeVar.OUTPUT()) then SimCodeVar.EXACT();
256 case (SimCodeVar.CONSTANT(), SimCodeVar.LOCAL()) then SimCodeVar.EXACT();
257
258 // FIXED
259 case (SimCodeVar.FIXED(), SimCodeVar.PARAMETER()) then SimCodeVar.EXACT();
260 case (SimCodeVar.FIXED(), SimCodeVar.CALCULATED_PARAMETER()) then SimCodeVar.CALCULATED();
261 case (SimCodeVar.FIXED(), SimCodeVar.LOCAL()) then SimCodeVar.CALCULATED();
262
263 // TUNABLE
264 case (SimCodeVar.TUNABLE(), SimCodeVar.PARAMETER()) then SimCodeVar.EXACT();
265 case (SimCodeVar.TUNABLE(), SimCodeVar.CALCULATED_PARAMETER()) then SimCodeVar.CALCULATED();
266 case (SimCodeVar.TUNABLE(), SimCodeVar.LOCAL()) then SimCodeVar.CALCULATED();
267
268 // DISCRETE
269 case (SimCodeVar.DISCRETE(), SimCodeVar.OUTPUT()) then SimCodeVar.CALCULATED();
270 case (SimCodeVar.DISCRETE(), SimCodeVar.LOCAL()) then SimCodeVar.CALCULATED();
271
272 // CONTINUOUS
273 case (SimCodeVar.CONTINUOUS(), SimCodeVar.OUTPUT()) then SimCodeVar.CALCULATED();
274 case (SimCodeVar.CONTINUOUS(), SimCodeVar.LOCAL()) then SimCodeVar.CALCULATED();
275
276 else SimCodeVar.NONE_INITIAL();
277 end match;
278 end getDefaultFmiInitialAttribute;
279
280 public function getFmiInitialAttributeStr
281 "This function is called from CodegenFMUCommon.tpl. It compares a variable's initial_ fmi attriute
282 with the default expected (based on teh variability and causality of the variable). If it turns out
283 to be the same as the default then it will return an empty string so that the value is not
284 printed to the modelDescription.xml file. However, if the flag DUMP_FORCE_FMI_ATTRIBUTES is set,
285 it will always print the attrbute whether it is equal to the defaul or not."
286 input SimCodeVar.SimVar simVar;
287 output String out_string = "";
288 protected
289 SimCodeVar.Initial var_initial, default_initial;
290 algorithm
291
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3484 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 3456 times.
3484 if isNone(simVar.initial_) then
292 28 return;
293 end if;
294
295 3456 SOME(var_initial) := simVar.initial_;
296 3456 default_initial := getDefaultFmiInitialAttribute(Util.getOptionOrDefault(simVar.variability, SimCodeVar.CONTINUOUS())
297 , Util.getOptionOrDefault(simVar.causality, SimCodeVar.LOCAL()));
298
299
3/4
✓ Branch 1 taken 2957 times.
✓ Branch 2 taken 499 times.
✓ Branch 4 taken 2957 times.
✗ Branch 5 not taken.
3456 if valueEq(var_initial, default_initial) and not Flags.isSet(Flags.DUMP_FORCE_FMI_ATTRIBUTES) then
300 var_initial := SimCodeVar.NONE_INITIAL(); // Set it to NONE_INITIAL here so the case below turns it to ""
301 end if;
302
303 out_string := match var_initial
304 case SimCodeVar.EXACT(__) then "exact";
305 case SimCodeVar.APPROX(__) then "approx";
306 case SimCodeVar.CALCULATED(__) then "calculated";
307 case SimCodeVar.NONE_INITIAL(__) then "";
308 end match;
309 end getFmiInitialAttributeStr;
310
311 public function simGenericCallString
312 input SimCode.SimGenericCall call;
313 output String str;
314 algorithm
315 str := match call
316 case SimCode.SINGLE_GENERIC_CALL() algorithm
317 373 str := "single generic call " + intString(call.index) + " " + List.toString(call.iters, simIteratorString);
318 373 str := str + "\n " + ExpressionBasics.printExpStr(call.lhs) + " = " + ExpressionBasics.printExpStr(call.rhs) + ";";
319 then str;
320
321 case SimCode.IF_GENERIC_CALL() algorithm
322 3 str := "if generic call " + intString(call.index) + " " + List.toString(call.iters, simIteratorString);
323 3 str := str + List.toString(call.branches, simBranchString, List.Style.NEWLINE);
324 then str;
325
326 case SimCode.WHEN_GENERIC_CALL() algorithm
327 1 str := "when generic call " + intString(call.index) + " " + List.toString(call.iters, simIteratorString);
328 1 str := str + List.toString(call.branches, simBranchString, List.Style.NEWLINE);
329 then str;
330
331 else "";
332 end match;
333 end simGenericCallString;
334
335 public function simBranchString
336 input SimCode.SimBranch branch;
337 output String str;
338 protected
339 function simBranchBodyString
340 input tuple<DAE.Exp, DAE.Exp> tpl;
341 output String str = ExpressionBasics.printExpStr(Util.tuple21(tpl)) + " = " + ExpressionBasics.printExpStr(Util.tuple22(tpl)) + ";";
342 end simBranchBodyString;
343 algorithm
344 str := match branch
345 local
346 Boolean b;
347
348 case SimCode.SIM_BRANCH() algorithm
349
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
6 b := isSome(branch.condition);
350 3 str := if b then "if " + ExpressionBasics.printExpStr(Util.getOption(branch.condition)) + " then\n" else "else\n";
351 6 str := str + List.toString(branch.body, simBranchBodyString, List.Style.NEWLINE_INDENT);
352
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 str := if b then str + "end if;" else str;
353 then str;
354
355 case SimCode.SIM_BRANCH_STMT() algorithm
356
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 b := isSome(branch.condition);
357 1 str := if b then "if " + ExpressionBasics.printExpStr(Util.getOption(branch.condition)) + " then\n" else "else\n";
358 1 str := str + List.toString(branch.body, DAEDump.ppStatementStr, List.Style.NEWLINE_INDENT);
359
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 str := if b then str + "\nend if;" else str;
360 then str;
361
362 else "";
363 end match;
364 end simBranchString;
365
366 public function simVarString
367 "returns the string representation of a SimVar in the following form
368 index: <index>: <name> (alias <aliasvar>) [ protected ][ hideResult ] initial: <initialValue>\tarrCref:<arrayCref> index:(<variable_index>) [<numArrayElement>]"
369 input SimCodeVar.SimVar inVar;
370 output String s;
371 algorithm
372 190 s := "index:" + intString(inVar.index) + ": " + ComponentReferenceBasics.printComponentRefStr(inVar.name);
373 s := s + (match inVar.aliasvar
374 local DAE.ComponentRef cr;
375 case SimCodeVar.NOALIAS() then " (no alias) ";
376 ✗ case SimCodeVar.ALIAS(varName = cr) then " (alias: " + ComponentReferenceBasics.printComponentRefStr(cr) + ") ";
377 ✗ case SimCodeVar.NEGATEDALIAS(varName = cr) then " (negated alias: " + ComponentReferenceBasics.printComponentRefStr(cr) + ") ";
378 end match);
379
2/2
✓ Branch 0 taken 188 times.
✓ Branch 1 taken 2 times.
378 s := s + (if inVar.isProtected then " protected " else "");
380
2/2
✓ Branch 1 taken 185 times.
✓ Branch 2 taken 5 times.
375 s := s + (if Util.getOptionOrDefault(inVar.hideResult, false) then " hideResult " else "");
381
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 190 times.
✓ Branch 3 taken 43 times.
✓ Branch 4 taken 147 times.
190 s := s + " initial: " + (if isSome(inVar.initialValue) then ExpressionDump.printOptExpStr(inVar.initialValue) else "");
382
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 190 times.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 170 times.
190 s := s + (if isSome(inVar.arrayCref) then "\tarrCref:" + ComponentReferenceBasics.printComponentRefStr(Util.getOption(inVar.arrayCref)) else "\tno arrCref");
383
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 190 times.
✓ Branch 3 taken 190 times.
✗ Branch 4 not taken.
190 s := s + " index:(" + (if isSome(inVar.variable_index) then intString(Util.getOption(inVar.variable_index)) else "") + ")";
384 190 s := s + " [" + stringDelimitList(inVar.numArrayElement, ",") + "]";
385 end simVarString;
386
387 public function setVariableIndexHelper
388 input list<SimCodeVar.SimVar> inVars;
389 input Integer inIndex;
390 input Integer inFMIIndex;
391 output list<SimCodeVar.SimVar> outVars;
392 output Integer outIndex;
393 output Integer outFMIIndex;
394 algorithm
395
2/2
✓ Branch 2 taken 25656 times.
✓ Branch 3 taken 74 times.
25730 (outVars, (outIndex, outFMIIndex)) := List.mapFold(inVars, setVariableIndexHelper2, (inIndex, inFMIIndex));
396 end setVariableIndexHelper;
397
398 protected function setVariableIndexHelper2
399 input output SimCodeVar.SimVar var;
400 input output tuple<Integer, Integer> tpl;
401 protected
402 Integer index, fmi_index;
403 algorithm
404 373394 (index, fmi_index) := tpl;
405
406 373394 var.variable_index := SOME(index);
407 373394 index := index + SimCodeUtilShared.getNumElems(var);
408
409
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 373394 times.
✓ Branch 2 taken 84015 times.
✓ Branch 3 taken 289379 times.
373394 if isSome(var.exportVar) then
410 289379 var.fmi_index := SOME(fmi_index);
411 289379 fmi_index := fmi_index + SimCodeUtilShared.getNumElems(var);
412 else
413 84015 var.fmi_index := NONE();
414 end if;
415
416 373394 tpl := (index, fmi_index);
417 end setVariableIndexHelper2;
418
419 public function unitConversion
420 "CevalScriptBackend's convertUnits: value_to = factor*value_from + offset, and
421 false where the two are of different dimensions."
422 input String to;
423 input String from;
424 output Boolean converts = false;
425 output Real factor = 1.0;
426 output Real offset = 0.0;
427 protected
428 UnitAbsyn.Unit u1, u2;
429 Real factor1, factor2, offset1, offset2;
430 algorithm
431 try
432 536 UnitParserExt.initSIUnits();
433 536 (u1, factor1, offset1) := UnitAbsynBuilder.str2unitWithScaleFactor(to, NONE());
434 536 (u2, factor2, offset2) := UnitAbsynBuilder.str2unitWithScaleFactor(from, NONE());
435
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 535 times.
536 true := valueEq(u1, u2);
436
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 535 times.
535 factor := factor2/factor1;
437 535 offset := (offset2 - offset1)/factor1;
438 converts := true;
439 else
440 end try;
441 end unitConversion;
442
443 public function createCrefToSimVarHT "author: unknown and marcusw
444 Create a hash table that maps all variable names (crefs) to the simVar objects."
445 input SimCode.ModelInfo modelInfo;
446 output SimCode.HashTableCrefToSimVar outHT;
447 protected
448 Integer size;
449 SimCode.VarInfo varInfo;
450 HashTableCrILst.HashTable arraySimVars;
451 SimCodeVar.SimVars vars;
452 algorithm
453 try
454 2211 varInfo := modelInfo.varInfo;
455 2211 vars := modelInfo.vars;
456 2211 size := varInfo.numStateVars + varInfo.numAlgVars + varInfo.numIntAlgVars + varInfo.numBoolAlgVars + varInfo.numAlgAliasVars +
457 varInfo.numIntAliasVars + varInfo.numBoolAliasVars + varInfo.numParams + varInfo.numIntParams + varInfo.numBoolParams +
458 varInfo.numOutVars + varInfo.numInVars + varInfo.numOptimizeConstraints + varInfo.numOptimizeFinalConstraints;
459 size := intMax(size, 1023);
460 2211 outHT := HashTableCrefSimVar.emptyHashTableSized(size);
461 2211 arraySimVars := HashTableCrILst.emptyHashTableSized(size);
462
463 2211 outHT := List.fold(vars.stateVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
464 //true := intLt(size, -1);
465 2211 outHT := List.fold(vars.derivativeVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
466 2211 outHT := List.fold(vars.algVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
467 2211 arraySimVars := List.fold(vars.algVars, getArraySimVars, arraySimVars);
468 2211 outHT := List.fold(vars.discreteAlgVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
469 2211 outHT := List.fold(vars.intAlgVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
470 2211 outHT := List.fold(vars.boolAlgVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
471 2211 outHT := List.fold(vars.paramVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
472 2211 arraySimVars := List.fold(vars.paramVars, getArraySimVars, arraySimVars);
473 2211 outHT := List.fold(vars.intParamVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
474 2211 outHT := List.fold(vars.boolParamVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
475 2211 outHT := List.fold(vars.aliasVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
476 2211 arraySimVars := List.fold(vars.aliasVars, getArraySimVars, arraySimVars);
477 2211 outHT := List.fold(vars.intAliasVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
478 2211 outHT := List.fold(vars.boolAliasVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
479 2211 outHT := List.fold(vars.stringAlgVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
480 2211 outHT := List.fold(vars.stringParamVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
481 2211 outHT := List.fold(vars.stringAliasVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
482 2211 outHT := List.fold(vars.extObjVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
483 2211 outHT := List.fold(vars.constVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
484 2211 outHT := List.fold(vars.intConstVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
485 2211 outHT := List.fold(vars.boolConstVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
486 2211 outHT := List.fold(vars.stringConstVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
487 2211 outHT := List.fold(vars.sensitivityVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
488 2211 outHT := List.fold(vars.jacobianVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
489 2211 outHT := List.fold(vars.seedVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
490 2211 outHT := List.fold(vars.realOptimizeConstraintsVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
491 2211 outHT := List.fold(vars.realOptimizeFinalConstraintsVars, HashTableCrefSimVar.addSimVarToHashTable, outHT);
492 else
493 ✗ Error.addInternalError("function createCrefToSimVarHT failed", sourceInfo());
494 ✗ fail();
495 end try;
496 end createCrefToSimVarHT;
497
498 protected function getArraySimVars "author: marcusw
499 store the array-cref of the variable in the hash table and add the variable-index as value. The variable is handled as array-variable,
500 if it has more than one element as numArrayElement."
501 input SimCodeVar.SimVar iSimVar;
502 input HashTableCrILst.HashTable iArrayMapping;
503 output HashTableCrILst.HashTable oArrayMapping;
504 protected
505 DAE.ComponentRef name;
506 DAE.ComponentRef arrayCref;
507 HashTableCrILst.HashTable tmpArrayMapping = iArrayMapping;
508 list<Integer> arrayVars;
509 Integer index;
510 algorithm
511 oArrayMapping := match iSimVar
512 case SimCodeVar.SIMVAR(name=name, index=index, numArrayElement=_::_)
513 algorithm
514 349417 arrayCref := ComponentReferenceBasics.crefStripLastSubs(name);
515
2/2
✓ Branch 1 taken 228188 times.
✓ Branch 2 taken 121229 times.
349417 if(BaseHashTable.hasKey(arrayCref, iArrayMapping)) then
516 228188 arrayVars := BaseHashTable.get(arrayCref, iArrayMapping);
517 228188 tmpArrayMapping := BaseHashTable.add((arrayCref, index::arrayVars), tmpArrayMapping);
518 else
519 121229 tmpArrayMapping := BaseHashTable.add((arrayCref, {index}), tmpArrayMapping);
520 end if;
521 //print("markSimVarArrays: " + ComponentReferenceBasics.printComponentRefStr(name) + " for " + ComponentReferenceBasics.printComponentRefStr(ComponentReferenceBasics.crefStripLastSubs(name)) + "\n");
522 then tmpArrayMapping;
523 else
524 then iArrayMapping;
525 end match;
526 end getArraySimVars;
527
528 public function functionInfo
529 input SimCodeFunction.Function fn;
530 output SourceInfo info;
531 algorithm
532 info := match fn
533 case SimCodeFunction.FUNCTION(info = info) then info;
534 case SimCodeFunction.EXTERNAL_FUNCTION(info = info) then info;
535 case SimCodeFunction.RECORD_CONSTRUCTOR(info = info) then info;
536 end match;
537 end functionInfo;
538
539 public function eqInfo
540 input SimCode.SimEqSystem eq;
541 output SourceInfo info;
542 algorithm
543 info := match eq
544 case SimCode.SES_RESIDUAL(source=DAE.SOURCE(info=info)) then info;
545 case SimCode.SES_FOR_RESIDUAL(source=DAE.SOURCE(info=info)) then info;
546 case SimCode.SES_GENERIC_RESIDUAL(source=DAE.SOURCE(info=info)) then info;
547 case SimCode.SES_SIMPLE_ASSIGN(source=DAE.SOURCE(info=info)) then info;
548 case SimCode.SES_SIMPLE_ASSIGN_CONSTRAINTS(source=DAE.SOURCE(info=info)) then info;
549 case SimCode.SES_ARRAY_CALL_ASSIGN(source=DAE.SOURCE(info=info)) then info;
550 case SimCode.SES_RESIZABLE_ASSIGN(source=DAE.SOURCE(info=info)) then info;
551 case SimCode.SES_GENERIC_ASSIGN(source=DAE.SOURCE(info=info)) then info;
552 case SimCode.SES_ENTWINED_ASSIGN(source=DAE.SOURCE(info=info)) then info;
553 case SimCode.SES_WHEN(source=DAE.SOURCE(info=info)) then info;
554 case SimCode.SES_FOR_LOOP(source=DAE.SOURCE(info=info)) then info;
555 end match;
556 end eqInfo;
557
558 public function simEqSystemIndex
559 input SimCode.SimEqSystem eq;
560 output Integer index;
561 algorithm
562 index := match eq
563 case SimCode.SES_RESIDUAL(index=index) then index;
564 case SimCode.SES_FOR_RESIDUAL(index=index) then index;
565 case SimCode.SES_GENERIC_RESIDUAL(index=index) then index;
566 case SimCode.SES_SIMPLE_ASSIGN(index=index) then index;
567 case SimCode.SES_SIMPLE_ASSIGN_CONSTRAINTS(index=index) then index;
568 case SimCode.SES_ARRAY_CALL_ASSIGN(index=index) then index;
569 case SimCode.SES_RESIZABLE_ASSIGN(index=index) then index;
570 case SimCode.SES_GENERIC_ASSIGN(index=index) then index;
571 case SimCode.SES_ENTWINED_ASSIGN(index=index) then index;
572 case SimCode.SES_IFEQUATION(index=index) then index;
573 case SimCode.SES_ALGORITHM(index=index) then index;
574 case SimCode.SES_INVERSE_ALGORITHM(index=index) then index;
575 case SimCode.SES_LINEAR(SimCode.LINEARSYSTEM(index=index)) then index;
576 case SimCode.SES_NONLINEAR(SimCode.NONLINEARSYSTEM(index=index)) then index;
577 case SimCode.SES_MIXED(index=index) then index;
578 case SimCode.SES_WHEN(index=index) then index;
579 case SimCode.SES_FOR_LOOP(index=index) then index;
580 case SimCode.SES_ALIAS(index=index) then index;
581 else
582 algorithm
583 ✗ Error.addMessage(Error.INTERNAL_ERROR,{"SimCodeUtil.simEqSystemIndex failed"});
584 ✗ then fail();
585 end match;
586 end simEqSystemIndex;
587
588 public function countDynamicExternalFunctions
589 input list<SimCodeFunction.Function> inFncLst;
590 output Integer outDynLoadFuncs;
591 algorithm
592 outDynLoadFuncs:= match inFncLst
593 local
594 list<SimCodeFunction.Function> rest;
595 Integer i;
596 case {}
597 then
598 0;
599 case SimCodeFunction.EXTERNAL_FUNCTION(dynamicLoad=true)::rest
600 algorithm
601 i := countDynamicExternalFunctions(rest);
602 then
603 intAdd(i, 1);
604 case _::rest
605 algorithm
606 106 i := countDynamicExternalFunctions(rest);
607 then
608 i;
609 end match;
610 end countDynamicExternalFunctions;
611
612 public function getVarIndexListByMapping "author: marcusw
613 Return the variable indices stored for the given variable in the mapping-table. If the variable is part of an array, all array indices are returned. This function is used by susan."
614 input HashTableCrIListArray.HashTable iVarToArrayIndexMapping;
615 input DAE.ComponentRef iVarName;
616 input Boolean iColumnMajor;
617 input String iIndexForUndefinedReferences;
618 output list<String> oVarIndexList; //if the variable is part of an array, all array indices are returned in this list (the list contains one element if the variable is a scalar)
619 algorithm
620 276 (oVarIndexList,_) := getVarIndexInfosByMapping(iVarToArrayIndexMapping, iVarName, iColumnMajor, iIndexForUndefinedReferences);
621 end getVarIndexListByMapping;
622
623 public function getVarIndexHeadByMapping "author: phannebohm
624 Return the head of variable indices stored for the given variable in the mapping-table, similar to getVarIndexListByMapping. This function is used by susan."
625 input HashTableCrIListArray.HashTable iVarToArrayIndexMapping;
626 input DAE.ComponentRef iVarName;
627 input Boolean iColumnMajor;
628 input String iIndexForUndefinedReferences;
629 output String oVarIndex;
630 protected
631 list<String> varIndexList;
632 algorithm
633 // TODO make this more efficient by not generating the whole varIndexList
634 7741 (varIndexList,_) := getVarIndexInfosByMapping(iVarToArrayIndexMapping, iVarName, iColumnMajor, iIndexForUndefinedReferences);
635 7741 oVarIndex := listHead(varIndexList);
636 end getVarIndexHeadByMapping;
637
638 public function getVarIndexByMapping "author: marcusw
639 Return the variable index stored for the given variable in the mapping-table. This function is used by susan."
640 input HashTableCrIListArray.HashTable iVarToArrayIndexMapping;
641 input DAE.ComponentRef iVarName;
642 input Boolean iColumnMajor;
643 input String iIndexForUndefinedReferences;
644 output String oConcreteVarIndex; //the scalar index of the variable (this value is always part of oVarIndexList)
645 algorithm
646 12278 (_,oConcreteVarIndex) := getVarIndexInfosByMapping(iVarToArrayIndexMapping, iVarName, iColumnMajor, iIndexForUndefinedReferences);
647 end getVarIndexByMapping;
648
649 public function providesDirectionalDerivative
650 input SimCode.SimCode inSimCode;
651 output Boolean b;
652 algorithm
653 b := match inSimCode
654 case SimCode.SIMCODE(modelStructure=SOME(SimCode.FMIMODELSTRUCTURE(continuousPartialDerivatives=SOME(_))))
655 then true;
656 else false;
657 end match;
658 end providesDirectionalDerivative;
659
660 protected function getVarIndexInfosByMapping "author: marcusw
661 Return the variable indices stored for the given variable in the mapping-table. This function is used by susan."
662 input HashTableCrIListArray.HashTable iVarToArrayIndexMapping;
663 input DAE.ComponentRef iVarName;
664 input Boolean iColumnMajor; //true if the subscripts should be evaluated in column major
665 input String iIndexForUndefinedReferences;
666 output list<String> oVarIndexList; //if the variable is part of an array, all array indices are returned in this list (the list contains one element if the variable is a scalar)
667 output String oConcreteVarIndex = ""; //the scalar index of the variable (this value is always part of oVarIndexList)
668 protected
669 DAE.ComponentRef varName = iVarName;
670 Integer arrayIdx, idx, arraySize, concreteVarIndex;
671 array<Integer> varIndices;
672 list<String> tmpVarIndexListNew = {};
673 list<DAE.Subscript> arraySubscripts;
674 list<Integer> arrayDimensions, arrayDimensionsReverse = {};
675 Boolean toColumnMajor;
676 Boolean isContiguous;
677 algorithm
678 20295 arraySubscripts := ComponentReference.crefLastSubs(varName);
679 20295 varName := ComponentReferenceBasics.crefStripLastSubs(varName);//removeSubscripts(varName);
680
1/2
✓ Branch 1 taken 20295 times.
✗ Branch 2 not taken.
20295 if(BaseHashTable.hasKey(varName, iVarToArrayIndexMapping)) then
681
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 20295 times.
20295 (arrayDimensions,varIndices) := BaseHashTable.get(varName, iVarToArrayIndexMapping); //varIndices are rowMajorOrder!
682 isContiguous := arrayLength(varIndices) == 1;
683
2/2
✓ Branch 0 taken 14097 times.
✓ Branch 1 taken 6198 times.
20295 if isContiguous then
684 14097 arraySize := List.fold(arrayDimensions, intMul, 1);
685 else
686 arraySize := arrayLength(varIndices);
687 end if;
688 20295 concreteVarIndex := SimCodeUtilShared.getScalarElementIndex(arraySubscripts, arrayDimensions);
689
3/4
✓ Branch 0 taken 20295 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1229 times.
✓ Branch 4 taken 19066 times.
20295 toColumnMajor := iColumnMajor and listLength(arrayDimensions) > 1;
690 if toColumnMajor then
691 1229 concreteVarIndex := convertIndexToColumnMajor(concreteVarIndex, arrayDimensions);
692 1229 arrayDimensionsReverse := listReverse(arrayDimensions);
693 end if;
694 //print("SimCodeUtil.getVarIndexInfosByMapping: Found variable index for '" + ComponentReferenceBasics.printComponentRefStr(iVarName) + "'. The value is " + intString(concreteVarIndex) + "\n");
695
1/2
✓ Branch 0 taken 20295 times.
✗ Branch 1 not taken.
106043 for arrayIdx in 0:(arraySize-1) loop
696 85748 idx := arraySize-arrayIdx;
697
2/2
✓ Branch 0 taken 10275 times.
✓ Branch 1 taken 75473 times.
85748 if toColumnMajor then
698 // convert to row major so that column major access will give this idx
699 10275 idx := convertIndexToColumnMajor(idx, arrayDimensionsReverse);
700 end if;
701
2/2
✓ Branch 0 taken 16835 times.
✓ Branch 1 taken 68913 times.
85748 if isContiguous then
702 16835 idx := arrayGet(varIndices, 1) + idx - 1;
703 else
704 68913 idx := arrayGet(varIndices, idx);
705 end if;
706
2/2
✓ Branch 0 taken 542 times.
✓ Branch 1 taken 85206 times.
85748 if(intLt(idx, 0)) then
707 542 tmpVarIndexListNew := intString((intMul(idx, -1) - 1))::tmpVarIndexListNew;
708 //print("SimCodeUtil.tmpVarIndexListNew: Warning, negativ aliases (" + ComponentReferenceBasics.printComponentRefStr(iVarName) + ") are not supported at the moment!\n");
709 else
710
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 85160 times.
85206 if(intEq(idx, 0)) then
711 tmpVarIndexListNew := iIndexForUndefinedReferences::tmpVarIndexListNew;
712 else
713 85160 tmpVarIndexListNew := intString(idx - 1)::tmpVarIndexListNew;
714 end if;
715 end if;
716 end for;
717
4/4
✓ Branch 1 taken 19629 times.
✓ Branch 2 taken 666 times.
✓ Branch 3 taken 1166 times.
✓ Branch 4 taken 18463 times.
20295 if isVarIndexListConsecutive(iVarToArrayIndexMapping,iVarName) and toColumnMajor then
718 //if the array is not completely stuffed (e.g. some array variables have been derived and became dummy-derivatives), the array will not be initialized as a consecutive array, therefore we cannot take the colMajor-indexes
719 // otherwise convert to column major for consecutive array
720 1166 concreteVarIndex := convertIndexToColumnMajor(concreteVarIndex, arrayDimensions);
721 end if;
722 20295 oConcreteVarIndex := listGet(tmpVarIndexListNew, concreteVarIndex);
723 end if;
724
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20295 times.
20295 if(listEmpty(tmpVarIndexListNew)) then
725 ✗ Error.addMessage(Error.INTERNAL_ERROR, {"GetVarIndexListByMapping: No Element for " + ComponentReferenceBasics.printComponentRefStr(varName) + " found!"});
726 tmpVarIndexListNew := {iIndexForUndefinedReferences};
727 oConcreteVarIndex := iIndexForUndefinedReferences;
728 end if;
729 //print("SimCodeUtil.getVarIndexInfosByMapping: Variable " + ComponentReferenceBasics.printComponentRefStr(iVarName) + " has variable indices {" + stringDelimitList(tmpVarIndexListNew, ",") + "} and concrete index " + oConcreteVarIndex + "\n");
730 oVarIndexList := tmpVarIndexListNew;
731 end getVarIndexInfosByMapping;
732
733 public function convertIndexToColumnMajor
734 "Converts row-major unrolled idx to column-major, author: rfranke"
735 input Integer idx; // one based, row-major ordered
736 input list<Integer> arrayDimensions;
737 output Integer idxOut; // one based, column-major ordered
738 protected
739 Integer idx0, ndim, length, idxi, fac;
740 algorithm
741 13164 ndim := listLength(arrayDimensions);
742 13164 length := List.fold(arrayDimensions, intMul, 1);
743 13164 idx0 := idx - 1; // zero based
744 idxOut := 1; // one based
745 fac := 1;
746
2/2
✓ Branch 0 taken 26778 times.
✓ Branch 1 taken 13164 times.
39942 for dimi in arrayDimensions loop
747
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26778 times.
26778 length := intDiv(length, dimi);
748
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26778 times.
26778 idxi := intDiv(idx0, length);
749 26778 idx0 := idx0 - idxi*length;
750 26778 idxOut := idxOut + idxi*fac;
751 26778 fac := fac * dimi;
752 end for;
753 end convertIndexToColumnMajor;
754
755 public function isVarIndexListConsecutive "author: marcusw
756 Check if all variable indices of the given variables, stored in the hash table, are consecutive."
757 input HashTableCrIListArray.HashTable iVarToArrayIndexMapping;
758 input DAE.ComponentRef iVarName;
759 output Boolean oIsConsecutive;
760 protected
761 DAE.ComponentRef varName = iVarName;
762 Integer arrayIdx, idx, arraySize;
763 Integer currentIndex = -1;
764 array<Integer> varIndices;
765 Boolean consecutive = true;
766 algorithm
767 22977 varName := ComponentReferenceBasics.crefStripLastSubs(varName);//removeSubscripts(varName);
768
1/2
✓ Branch 1 taken 22977 times.
✗ Branch 2 not taken.
22977 if(BaseHashTable.hasKey(varName, iVarToArrayIndexMapping)) then
769
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22977 times.
22977 (_,varIndices) := BaseHashTable.get(varName, iVarToArrayIndexMapping);
770 arraySize := arrayLength(varIndices);
771
1/2
✓ Branch 0 taken 22977 times.
✗ Branch 1 not taken.
116119 for arrayIdx in 0:(arraySize-1) loop
772 93142 idx := arrayGet(varIndices, arraySize-arrayIdx);
773
2/2
✓ Branch 0 taken 662 times.
✓ Branch 1 taken 92480 times.
93142 if(intLt(idx, 0)) then
774
2/2
✓ Branch 0 taken 557 times.
✓ Branch 1 taken 105 times.
662 if(intEq(currentIndex, -1)) then
775 557 currentIndex := intMul(idx, -1) - 1;
776 else
777
3/4
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 93 times.
105 consecutive := boolAnd(consecutive, intEq(currentIndex, intMul(idx, -1)));
778 105 currentIndex := intMul(idx, -1) - 1;
779 end if;
780 //print("SimCodeUtil.isVarIndexListConsecutive: Warning, negativ aliases (" + ComponentReferenceBasics.printComponentRefStr(iVarName) + ") are not supported at the moment!\n");
781 else
782
2/2
✓ Branch 0 taken 92390 times.
✓ Branch 1 taken 90 times.
92480 if(intEq(idx, 0)) then
783 currentIndex := -2;
784 consecutive := false;
785 else
786
2/2
✓ Branch 0 taken 22420 times.
✓ Branch 1 taken 69970 times.
92390 if(intEq(currentIndex, -1)) then
787 22420 currentIndex := idx - 1;
788 else
789 //print("SimCodeUtil.isVarIndexListConsecutive: Checking if " + intString(currentIndex) + " is consecutive with " + intString(idx) + "\n");
790 69970 consecutive := boolAnd(consecutive, intEq(currentIndex, idx));
791 //print("SimCodeUtil.isVarIndexListConsecutive: " + boolString(consecutive) + "\n");
792 69970 currentIndex := idx - 1;
793 end if;
794 end if;
795 end if;
796 end for;
797 end if;
798 oIsConsecutive := consecutive;
799 end isVarIndexListConsecutive;
800
801 public function getEnumerationTypes
802 input SimCodeVar.SimVars inVars;
803 output list<SimCodeVar.SimVar> outVars;
804 algorithm
805 outVars := match inVars
806 case SimCodeVar.SIMVARS()
807 algorithm
808 91 outVars := getEnumerationTypesHelper(inVars.stateVars, {});
809 91 outVars := getEnumerationTypesHelper(inVars.derivativeVars, outVars);
810 91 outVars := getEnumerationTypesHelper(inVars.algVars, outVars);
811 91 outVars := getEnumerationTypesHelper(inVars.discreteAlgVars, outVars);
812 91 outVars := getEnumerationTypesHelper(inVars.intAlgVars, outVars);
813 91 outVars := getEnumerationTypesHelper(inVars.boolAlgVars, outVars);
814 91 outVars := getEnumerationTypesHelper(inVars.inputVars, outVars);
815 91 outVars := getEnumerationTypesHelper(inVars.outputVars, outVars);
816 91 outVars := getEnumerationTypesHelper(inVars.aliasVars, outVars);
817 91 outVars := getEnumerationTypesHelper(inVars.intAliasVars, outVars);
818 91 outVars := getEnumerationTypesHelper(inVars.boolAliasVars, outVars);
819 91 outVars := getEnumerationTypesHelper(inVars.paramVars, outVars);
820 91 outVars := getEnumerationTypesHelper(inVars.intParamVars, outVars);
821 91 outVars := getEnumerationTypesHelper(inVars.boolParamVars, outVars);
822 91 outVars := getEnumerationTypesHelper(inVars.stringAlgVars, outVars);
823 91 outVars := getEnumerationTypesHelper(inVars.stringParamVars, outVars);
824 91 outVars := getEnumerationTypesHelper(inVars.stringAliasVars, outVars);
825 91 outVars := getEnumerationTypesHelper(inVars.extObjVars, outVars);
826 91 outVars := getEnumerationTypesHelper(inVars.constVars, outVars);
827 91 outVars := getEnumerationTypesHelper(inVars.intConstVars, outVars);
828 91 outVars := getEnumerationTypesHelper(inVars.boolConstVars, outVars);
829 91 outVars := getEnumerationTypesHelper(inVars.stringConstVars, outVars);
830 91 outVars := getEnumerationTypesHelper(inVars.sensitivityVars, outVars);
831 91 outVars := getEnumerationTypesHelper(inVars.jacobianVars, outVars);
832 91 outVars := getEnumerationTypesHelper(inVars.seedVars, outVars);
833 91 outVars := getEnumerationTypesHelper(inVars.realOptimizeConstraintsVars, outVars);
834 91 outVars := getEnumerationTypesHelper(inVars.realOptimizeFinalConstraintsVars, outVars);
835 91 then
836 listReverse(outVars); // TODO: Is the order actually important?
837
838 else {};
839 end match;
840 end getEnumerationTypes;
841
842 protected function getEnumerationTypesHelper
843 input list<SimCodeVar.SimVar> inVars;
844 input list<SimCodeVar.SimVar> inAccumVars;
845 output list<SimCodeVar.SimVar> outVars = inAccumVars;
846 algorithm
847
2/2
✓ Branch 0 taken 10982 times.
✓ Branch 1 taken 2457 times.
13439 for var in inVars loop
848 () := match var
849 case SimCodeVar.SIMVAR()
850 algorithm
851 // Add the variable to the list if it's an enumeration variable which
852 // doesn't already exist in the list.
853
4/4
✓ Branch 1 taken 53 times.
✓ Branch 2 taken 10929 times.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 26 times.
10982 if Types.isEnumeration(var.type_) and not
854 List.exist1(outVars, enumerationTypeExists, var.type_) then
855 outVars := var :: outVars;
856 end if;
857 then
858 ();
859
860 else ();
861 end match;
862 end for;
863 end getEnumerationTypesHelper;
864
865 protected function enumerationTypeExists
866 input SimCodeVar.SimVar var;
867 input DAE.Type inType;
868 output Boolean b;
869 algorithm
870 b := match (var, inType)
871 local
872 DAE.Type ty;
873
874 case (SimCodeVar.SIMVAR(type_ = ty as DAE.T_ENUMERATION()), DAE.T_ENUMERATION())
875 53 then AbsynUtil.pathEqual(ty.path, inType.path);
876 else false;
877 end match;
878 end enumerationTypeExists;
879
880 public function getSimEqSysForIndex
881 input Integer idx;
882 input list<SimCode.SimEqSystem> allSimEqs;
883 output SimCode.SimEqSystem outSimEq;
884 algorithm
885 try
886 ✗ outSimEq := List.getMemberOnTrue(idx,allSimEqs,indexIsEqual);
887 else
888 ✗ print("getSimEqSysForIndex failed!\n");
889 ✗ fail();
890 end try;
891 end getSimEqSysForIndex;
892
893 public function indexIsEqual
894 input Integer idx;
895 input SimCode.SimEqSystem ses;
896 output Boolean b;
897 protected
898 Integer idx2;
899 algorithm
900 ✗ idx2 := simEqSystemIndex(ses);
901 ✗ b := intEq(idx,idx2);
902 end indexIsEqual;
903
904 public function getSimEqSystemCrefsLHS "gets the crefs of the vars that are assigned (the lhs) for a simEqSystem
905 author:Waurich TUD 2014-05"
906 input SimCode.SimEqSystem simEqSys;
907 output list<DAE.ComponentRef> crefsOut;
908 algorithm
909 crefsOut := match simEqSys
910 local
911 DAE.Exp lhs;
912 DAE.ComponentRef cref;
913 list<DAE.ComponentRef> crefs, crefs2;
914 list<SimCodeVar.SimVar> simVars;
915 list<SimCode.SimEqSystem> residual;
916 case SimCode.SES_RESIDUAL()
917 algorithm
918 ✗ print("implement SES_RESIDUAL in SimCodeUtil.getSimEqSystemCrefsLHS!\n");
919 then {};
920 case SimCode.SES_SIMPLE_ASSIGN(cref=cref)
921 then {cref};
922 case SimCode.SES_SIMPLE_ASSIGN_CONSTRAINTS(cref=cref)
923 then {cref};
924 case SimCode.SES_ARRAY_CALL_ASSIGN(lhs=lhs)
925 ✗ then {Expression.expCref(lhs)};
926 case SimCode.SES_IFEQUATION()
927 algorithm
928 ✗ print("implement SES_IFEQUATION in SimCodeUtil.getSimEqSystemCrefsLHS!\n");
929 then {};
930 case SimCode.SES_ALGORITHM() algorithm
931 ✗ print("implement SES_ALGORITHM in SimCodeUtil.getSimEqSystemCrefsLHS!\n");
932 then {};
933 case SimCode.SES_INVERSE_ALGORITHM() algorithm
934 ✗ print("implement SES_INVERSE_ALGORITHM in SimCodeUtil.getSimEqSystemCrefsLHS!\n");
935 then {};
936 case SimCode.SES_LINEAR(SimCode.LINEARSYSTEM(vars=simVars,residual=residual))
937 algorithm
938 ✗ crefs2 := list(v.name for v in simVars);
939 ✗ then listAppend(crefs2,crefs2);
940 case SimCode.SES_NONLINEAR(SimCode.NONLINEARSYSTEM(crefs=crefs))
941 then crefs;
942 case SimCode.SES_MIXED(discVars=simVars)
943 ✗ then list(v.name for v in simVars);
944 case SimCode.SES_WHEN(whenStmtLst={BackendDAE.ASSIGN(left=lhs)})
945 algorithm
946 ✗ crefs := Expression.getAllCrefs(lhs);
947 then crefs;
948 end match;
949 end getSimEqSystemCrefsLHS;
950
951 public function getMaxSimEqSystemIndex"gets the maximal index of all simEqSystems in the SimCode.
952 author:Waurich TUD 2014-06"
953 input SimCode.SimCode simCode;
954 output Integer idxOut = 0;
955 protected
956 list<SimCode.SimEqSystem> allEquations,jacobianEquations,equationsForZeroCrossings,algorithmAndEquationAsserts,removedEquations,parameterEquations,maxValueEquations,minValueEquations,nominalValueEquations,startValueEquations,initialEquations;
957 list<list<SimCode.SimEqSystem>> odeEquations, algebraicEquations;
958 algorithm
959 ✗ SimCode.SIMCODE(allEquations = allEquations, odeEquations=odeEquations, algebraicEquations=algebraicEquations, initialEquations=initialEquations,
960 startValueEquations=startValueEquations, nominalValueEquations=nominalValueEquations, minValueEquations=minValueEquations, maxValueEquations=maxValueEquations,
961 parameterEquations=parameterEquations, removedEquations=removedEquations, algorithmAndEquationAsserts=algorithmAndEquationAsserts,
962 equationsForZeroCrossings=equationsForZeroCrossings, jacobianEquations=jacobianEquations) := simCode;
963 ✗ for eq in jacobianEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
964 ✗ for eq in equationsForZeroCrossings loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
965 ✗ for eq in algorithmAndEquationAsserts loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
966 ✗ for eq in removedEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
967 ✗ for eq in parameterEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
968 ✗ for eq in maxValueEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
969 ✗ for eq in minValueEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
970 ✗ for eq in nominalValueEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
971 ✗ for eq in nominalValueEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
972 ✗ for eq in startValueEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
973 ✗ for eq in initialEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
974 ✗ for eq in allEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
975 ✗ for eq in jacobianEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
976 ✗ for eq in jacobianEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
977 ✗ for eq in jacobianEquations loop idxOut := intMax(idxOut, simEqSystemIndex(eq)); end for;
978 end getMaxSimEqSystemIndex;
979
980 public function getDaeEqsNotPartOfOdeSystem "Get a list of eqSystem-objects that are solved in DAE, but not in the ODE-system.
981 author: marcusw"
982 input SimCode.SimCode iSimCode;
983 output list<SimCode.SimEqSystem> oEqs;
984 protected
985 array<Option<SimCode.SimEqSystem>> allEqs;
986 list<tuple<Integer, SimCode.SimEqSystem>> allEqIdxMapping; //mapping SimEqIdx -> SimEqSystem
987 list<SimCode.SimEqSystem> allEquations;
988 list<list<SimCode.SimEqSystem>> odeEquations;
989 Integer highestIdx;
990 list<SimCode.SimEqSystem> tmpEqs;
991 algorithm
992 ✗ SimCode.SIMCODE(allEquations=allEquations,odeEquations=odeEquations) := iSimCode;
993 ✗ (allEqIdxMapping, highestIdx) := List.fold(allEquations, getDaeEqsNotPartOfOdeSystem0, ({}, 0));
994 ✗ allEqs := arrayCreate(highestIdx, NONE());
995 ✗ allEqs := List.fold(allEqIdxMapping, getDaeEqsNotPartOfOdeSystem1, allEqs);
996 ✗ allEqs := List.fold(odeEquations, getDaeEqsNotPartOfOdeSystem2, allEqs);
997 tmpEqs := {};
998 ✗ tmpEqs := Array.fold(allEqs, getDaeEqsNotPartOfOdeSystem4, tmpEqs);
999 ✗ oEqs := Dangerous.listReverseInPlace(tmpEqs);
1000 end getDaeEqsNotPartOfOdeSystem;
1001
1002 protected function getDaeEqsNotPartOfOdeSystem0 "Add the given equation system object to the mapping list (simEqIdx -> SimEqSystem).
1003 author: marcusw"
1004 input SimCode.SimEqSystem iEqSystem;
1005 input tuple<list<tuple<Integer, SimCode.SimEqSystem>>, Integer> iMappingWithHighestIdx; //<mapping simEqIdx -> SimEqSystem, highestIdx>
1006 output tuple<list<tuple<Integer, SimCode.SimEqSystem>>, Integer> outMappingWithHighestIdx;
1007 protected
1008 Integer index, highestIdx;
1009 list<tuple<Integer, SimCode.SimEqSystem>> allEqIdxMapping;
1010 algorithm
1011 ✗ index := simEqSystemIndex(iEqSystem);
1012 ✗ (allEqIdxMapping, highestIdx) := iMappingWithHighestIdx;
1013 ✗ allEqIdxMapping := (index, iEqSystem)::allEqIdxMapping;
1014 highestIdx := intMax(highestIdx, index);
1015 ✗ outMappingWithHighestIdx := (allEqIdxMapping, highestIdx);
1016 end getDaeEqsNotPartOfOdeSystem0;
1017
1018 protected function getDaeEqsNotPartOfOdeSystem1 "Set the array at position simEqIdx to the simEqSystem-object.
1019 author: marcusw"
1020 input tuple<Integer, SimCode.SimEqSystem> iEqSystem; //<simEqIdx, simEqSystem>
1021 input array<Option<SimCode.SimEqSystem>> iEqArray;
1022 output array<Option<SimCode.SimEqSystem>> oEqArray;
1023 protected
1024 Integer eqSysIdx;
1025 SimCode.SimEqSystem eqSys;
1026 algorithm
1027 ✗ (eqSysIdx, eqSys) := iEqSystem;
1028 ✗ oEqArray := arrayUpdate(iEqArray, eqSysIdx, SOME(eqSys));
1029 end getDaeEqsNotPartOfOdeSystem1;
1030
1031 protected function getDaeEqsNotPartOfOdeSystem2 "Set the array at position simEqIdx to NONE().
1032 author: marcusw"
1033 input list<SimCode.SimEqSystem> iEqSystem;
1034 input array<Option<SimCode.SimEqSystem>> iEqArray;
1035 output array<Option<SimCode.SimEqSystem>> oEqArray;
1036 algorithm
1037 ✗ oEqArray := List.fold(iEqSystem, getDaeEqsNotPartOfOdeSystem3, iEqArray);
1038 end getDaeEqsNotPartOfOdeSystem2;
1039
1040 protected function getDaeEqsNotPartOfOdeSystem3 "Set the array at position simEqIdx to NONE().
1041 author: marcusw"
1042 input SimCode.SimEqSystem iEqSystem;
1043 input array<Option<SimCode.SimEqSystem>> iEqArray;
1044 output array<Option<SimCode.SimEqSystem>> oEqArray;
1045 protected
1046 Integer eqSysIdx;
1047 algorithm
1048 ✗ eqSysIdx := simEqSystemIndex(iEqSystem);
1049 ✗ oEqArray := arrayUpdate(iEqArray, eqSysIdx, NONE());
1050 end getDaeEqsNotPartOfOdeSystem3;
1051
1052 protected function getDaeEqsNotPartOfOdeSystem4 "Append the element to the list if it is not NONE().
1053 author: marcusw"
1054 input Option<SimCode.SimEqSystem> iEqSystemOpt;
1055 input list<SimCode.SimEqSystem> iResList;
1056 output list<SimCode.SimEqSystem> oResList;
1057 protected
1058 SimCode.SimEqSystem eqSys;
1059 algorithm
1060 oResList := match iEqSystemOpt
1061 case SOME(eqSys)
1062 then eqSys::iResList;
1063 else
1064 then iResList;
1065 end match;
1066 end getDaeEqsNotPartOfOdeSystem4;
1067
1068 public function getStateSimVarIndexFromIndex
1069 input list<SimCodeVar.SimVar> inStateVars;
1070 input Integer inIndex;
1071 output Integer outVariableIndex;
1072 protected
1073 SimCodeVar.SimVar stateVar;
1074 algorithm
1075
3/4
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 76 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
82 stateVar := listGet(inStateVars, inIndex + 1 - (if (Config.simCodeTarget()=="Cpp" ) then 0 else listLength(inStateVars)) /* SimVar indexes start from zero */);
1076 82 outVariableIndex := getVariableIndex(stateVar);
1077 end getStateSimVarIndexFromIndex;
1078
1079 public function getNumScalars
1080 "Get number of elements when rolling out all arrays of a variable list.
1081 author: rfranke"
1082 input list<SimCodeVar.SimVar> vars;
1083 output Integer numScalars;
1084 algorithm
1085 37647 numScalars := List.applyAndFold(vars, intAdd, SimCodeUtilShared.getNumElems, 0);
1086 end getNumScalars;
1087
1088 public function numScalarElems
1089 "Total number of scalar elements over a list of SimVars (rolling out arrays).
1090 Equals listLength for scalarized variables. Public wrapper around getNumScalars
1091 used by the FMU templates to compute per-scalar NUMBER_OF_* sizes for
1092 non-scalarized arrays."
1093 input list<SimCodeVar.SimVar> vars;
1094 output Integer n;
1095 algorithm
1096 17336 n := getNumScalars(vars);
1097 end numScalarElems;
1098
1099 public function jacobianIndexExp
1100 "The position of a variable of a Jacobian in its seed, tmp or result array:
1101 its index, unless a variable before it in the same array has a size that is
1102 only known at runtime (resizable arrays). Then it is the sum of the sizes of
1103 these variables, an expression of the structural parameters."
1104 input SimCodeVar.SimVar var;
1105 input HashTableCrefSimVar.HashTable ht;
1106 output DAE.Exp exp = DAE.ICONST(var.index);
1107 protected
1108 list<SimCodeVar.SimVar> vars, before = {};
1109 UnorderedSet<Integer> seen;
1110 algorithm
1111 // only non-scalarized arrays can have a size that is known at runtime
1112
4/4
✓ Branch 0 taken 63632 times.
✓ Branch 1 taken 12155 times.
✓ Branch 3 taken 61352 times.
✓ Branch 4 taken 2280 times.
75787 if var.index <= 0 or Flags.getConfigBool(Flags.SIM_CODE_SCALARIZE) then
1113 73507 return;
1114 end if;
1115 2280 vars := BaseHashTable.hashTableValueList(ht);
1116
2/2
✓ Branch 1 taken 2174 times.
✓ Branch 2 taken 106 times.
2280 if not List.any(vars, isSymbolicArrayVar) then
1117 2174 return;
1118 end if;
1119 106 seen := UnorderedSet.new(Util.id, intEq);
1120
2/2
✓ Branch 0 taken 910 times.
✓ Branch 1 taken 106 times.
1016 for v in vars loop
1121
7/8
✓ Branch 0 taken 862 times.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 535 times.
✓ Branch 3 taken 327 times.
✓ Branch 5 taken 315 times.
✓ Branch 6 taken 220 times.
✓ Branch 8 taken 315 times.
✗ Branch 9 not taken.
910 if v.index >= 0 and v.index < var.index and valueEq(v.varKind, var.varKind) and not UnorderedSet.contains(v.index, seen) then
1122 315 UnorderedSet.add(v.index, seen);
1123 before := v :: before;
1124 end if;
1125 end for;
1126
2/2
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 63 times.
106 if List.any(before, isSymbolicArrayVar) then
1127 exp := DAE.ICONST(0);
1128
2/2
✓ Branch 0 taken 234 times.
✓ Branch 1 taken 63 times.
297 for v in before loop
1129 234 exp := DAE.BINARY(exp, DAE.ADD(DAE.T_INTEGER_DEFAULT), simVarSizeExp(v));
1130 end for;
1131 end if;
1132 end jacobianIndexExp;
1133
1134 public function simVarSizeExp
1135 "The number of scalar elements of a SimVar as an expression."
1136 input SimCodeVar.SimVar var;
1137 output DAE.Exp exp = DAE.ICONST(1);
1138 algorithm
1139
2/2
✓ Branch 1 taken 375 times.
✓ Branch 2 taken 270 times.
645 for d in Expression.arrayDimension(var.type_) loop
1140 exp := DAE.BINARY(exp, DAE.MUL(DAE.T_INTEGER_DEFAULT), match d
1141 195 case DAE.DIM_EXP() then d.exp;
1142 180 else DAE.ICONST(Expression.dimensionSize(d));
1143 end match);
1144 end for;
1145 end simVarSizeExp;
1146
1147 public function simVarDimExps
1148 "The dimensions of a SimVar as expressions, outermost first."
1149 input SimCodeVar.SimVar var;
1150 output list<DAE.Exp> exps;
1151 algorithm
1152 ✗ exps := list(match d
1153 ✗ case DAE.DIM_EXP() then d.exp;
1154 ✗ else DAE.ICONST(Expression.dimensionSize(d));
1155 end match for d in Expression.arrayDimension(var.type_));
1156 end simVarDimExps;
1157
1158 public function isWholeResizableArray
1159 "true if an iteration variable of an algebraic loop is a whole array whose
1160 size is only known at runtime (resizable arrays): the loop has to be sized at
1161 runtime then."
1162 input DAE.ComponentRef cr;
1163 input SimCodeVar.SimVar var;
1164 output Boolean b = not ComponentReference.crefHaveSubs(cr) and isSymbolicArrayVar(var);
1165 end isWholeResizableArray;
1166
1167 public function residualOffsetExp
1168 "The position of the n-th residual (zero-based, counting only the residual
1169 equations, like the index of the residual template) of an algebraic loop in
1170 its residual vector: the sum of the sizes of the residuals before it, an
1171 expression of the size parameters for resizable arrays."
1172 input list<SimCode.SimEqSystem> eqs;
1173 input Integer n;
1174 output DAE.Exp exp = DAE.ICONST(0);
1175 protected
1176 Integer count = 0;
1177 Option<DAE.Exp> osz;
1178 DAE.Exp sz;
1179 algorithm
1180
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 for eq in eqs loop
1181
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 if count >= n then
1182 break;
1183 end if;
1184 osz := match eq
1185 2 case SimCode.SES_RESIDUAL() then SOME(typeSizeExp(Expression.typeof(eq.exp)));
1186 case SimCode.SES_FOR_RESIDUAL() algorithm
1187 1 sz := typeSizeExp(Expression.typeof(eq.exp));
1188
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 for it in eq.iterators loop
1189 1 sz := DAE.BINARY(sz, DAE.MUL(DAE.T_INTEGER_DEFAULT), simIteratorSizeExp(it));
1190 end for;
1191 then SOME(sz);
1192 ✗ case SimCode.SES_GENERIC_RESIDUAL() then SOME(DAE.ICONST(listLength(eq.scal_indices)));
1193 else NONE();
1194 end match;
1195
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 6 times.
9 if isSome(osz) then
1196 3 SOME(sz) := osz;
1197 3 exp := DAE.BINARY(exp, DAE.ADD(DAE.T_INTEGER_DEFAULT), sz);
1198 3 count := count + 1;
1199 end if;
1200 end for;
1201 end residualOffsetExp;
1202
1203 protected function simIteratorSizeExp
1204 input BackendDAE.SimIterator it;
1205 output DAE.Exp exp;
1206 algorithm
1207 exp := match it
1208 1 case BackendDAE.SIM_ITERATOR_RANGE() then it.size;
1209 ✗ case BackendDAE.SIM_ITERATOR_LIST() then DAE.ICONST(it.size);
1210 end match;
1211 end simIteratorSizeExp;
1212
1213 protected function typeSizeExp
1214 "the number of scalar elements of a type as an expression"
1215 input DAE.Type ty;
1216 output DAE.Exp exp = DAE.ICONST(1);
1217 algorithm
1218
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 for d in Expression.arrayDimension(ty) loop
1219 exp := DAE.BINARY(exp, DAE.MUL(DAE.T_INTEGER_DEFAULT), match d
1220 ✗ case DAE.DIM_EXP() then d.exp;
1221 ✗ else DAE.ICONST(Expression.dimensionSize(d));
1222 end match);
1223 end for;
1224 end typeSizeExp;
1225
1226 public function numScalarElemsBeforeExp
1227 "Like numScalarElemsBefore as an expression: the scalar offset of the n-th
1228 variable (zero-based), with the sizes of resizable arrays as expressions of
1229 their size parameters."
1230 input list<SimCodeVar.SimVar> vars;
1231 input Integer n;
1232 output DAE.Exp exp = DAE.ICONST(0);
1233 algorithm
1234 ✗ for v in List.firstN(vars, n) loop
1235 ✗ exp := DAE.BINARY(exp, DAE.ADD(DAE.T_INTEGER_DEFAULT), simVarSizeExp(v));
1236 end for;
1237 end numScalarElemsBeforeExp;
1238
1239 public function isDimensionParameter
1240 "true for a size parameter $DIM_k of a derived dimension of a resizable array,
1241 see NBResizable.addDimensionParameters. Its start value is the expression of
1242 the dimension."
1243 input SimCodeVar.SimVar var;
1244 output Boolean b;
1245 algorithm
1246 b := match var
1247 case SimCodeVar.SIMVAR(varKind = BackendDAE.PARAM(), initialValue = SOME(_))
1248 24386 then StringUtil.startsWith(ComponentReferenceBasics.printComponentRefStr(var.name), "$DIM_");
1249 else false;
1250 end match;
1251 end isDimensionParameter;
1252
1253 public function jacobianResultVars
1254 "The result variables of a Jacobian with the resizable sparsity pattern of the
1255 new backend: the variables its rows are solved for, each once. Empty if one of
1256 them is not in the Jacobian's variables."
1257 input SimCode.Sparsity sparsity;
1258 input Option<HashTableCrefSimVar.HashTable> crefsHT;
1259 output list<SimCodeVar.SimVar> vars = {};
1260 protected
1261 HashTableCrefSimVar.HashTable ht;
1262 list<DAE.ComponentRef> crefs = {};
1263 list<SimCode.SparsityRow> rows;
1264 algorithm
1265 try
1266
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 180 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 180 times.
180 SOME(ht) := crefsHT;
1267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 180 times.
180 SimCode.SPARSITY(rows = rows) := sparsity;
1268
2/2
✓ Branch 0 taken 885 times.
✓ Branch 1 taken 180 times.
1065 for row in rows loop
1269
2/2
✓ Branch 0 taken 886 times.
✓ Branch 1 taken 885 times.
1771 for cr in row.solved_crefs loop
1270 886 crefs := ComponentReference.crefStripSubs(cr) :: crefs;
1271 end for;
1272 end for;
1273 180 crefs := List.unique(listReverse(crefs));
1274
4/4
✓ Branch 0 taken 862 times.
✓ Branch 1 taken 180 times.
✓ Branch 2 taken 862 times.
✓ Branch 3 taken 180 times.
1042 vars := list(BaseHashTable.get(cr, ht) for cr in crefs);
1275 else
1276 vars := {};
1277 end try;
1278 end jacobianResultVars;
1279
1280 public function hasSymbolicDims
1281 "true if an array SimVar has a dimension that is no integer literal, e.g. the
1282 parameter N of a resizable array (--resizableArrays). Its number of elements is
1283 only known at runtime."
1284 input list<SimCodeVar.SimVar> vars;
1285 output Boolean b = List.any(vars, isSymbolicArrayVar);
1286 end hasSymbolicDims;
1287
1288 public function isSymbolicArrayVar
1289 "true if an array SimVar has a dimension that is no integer literal"
1290 input SimCodeVar.SimVar var;
1291 output Boolean b;
1292 algorithm
1293 b := match var
1294 15779 case SimCodeVar.SIMVAR(type_ = DAE.T_ARRAY()) then not List.all(var.numArrayElement, isIntegerString);
1295 else false;
1296 end match;
1297 end isSymbolicArrayVar;
1298
1299 protected function isIntegerString
1300 input String s;
1301 output Boolean b;
1302 algorithm
1303 try
1304 22227 _ := stringInt(s);
1305 b := true;
1306 else
1307 b := false;
1308 end try;
1309 end isIntegerString;
1310
1311 public function numScalarElemsBefore
1312 "Total number of scalar elements of the first n SimVars of a list. The
1313 scalar offset of the n-th variable (zero-based) when rolling out arrays."
1314 input list<SimCodeVar.SimVar> vars;
1315 input Integer n;
1316 output Integer numScalars = 0;
1317 algorithm
1318
2/2
✓ Branch 1 taken 2620 times.
✓ Branch 2 taken 1172 times.
3792 for v in List.firstN(vars, n) loop
1319 2620 numScalars := numScalars + SimCodeUtilShared.getNumElems(v);
1320 end for;
1321 end numScalarElemsBefore;
1322
1323 public function numScalarElemsVar
1324 "Number of scalar elements of a SimVar, rolling out arrays."
1325 input SimCodeVar.SimVar var;
1326 output Integer n = SimCodeUtilShared.getNumElems(var);
1327 end numScalarElemsVar;
1328
1329 public function arrayElementSubscripts
1330 "Subscripts of all elements of an array SimVar in row-major order, e.g.
1331 {\"1,1\", \"1,2\", \"2,1\", \"2,2\"} for a 2x2 matrix. Empty for scalars."
1332 input SimCodeVar.SimVar var;
1333 output list<String> subscripts = {};
1334 protected
1335 list<Integer> dims;
1336 list<list<String>> acc = {{}};
1337 algorithm
1338 subscripts := match var
1339 case SimCodeVar.SIMVAR(type_ = DAE.T_ARRAY()) algorithm
1340 ✗ dims := list(stringInt(d) for d in var.numArrayElement);
1341 ✗ for d in listReverse(dims) loop
1342 ✗ acc := List.flatten(list(list(intString(i) :: rest for rest in acc) for i in 1:d));
1343 end for;
1344 ✗ then list(stringDelimitList(sub, ",") for sub in acc);
1345 else {};
1346 end match;
1347 end arrayElementSubscripts;
1348
1349 public function getFMI3ArrayStart
1350 "Space separated list of scalar start values for an FMI 3.0 array variable
1351 (length = number of scalar elements). Element-wise start values (e.g.
1352 start = {1,2,3}) are listed per element; a single (broadcast) value, as
1353 produced by 'each start = ...', is repeated for every element. Returns the
1354 empty string when there is no start value."
1355 input SimCodeVar.SimVar var;
1356 output String out = "";
1357 protected
1358 list<String> svals;
1359 Integer n;
1360 algorithm
1361 out := match var.initialValue
1362 local DAE.Exp e;
1363 case SOME(e) algorithm
1364 55 svals := getFMIArrayStartValues(e);
1365 55 n := SimCodeUtilShared.getNumElems(var);
1366
3/4
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 54 times.
55 then
1367 if listEmpty(svals) then ""
1368 // a single (broadcast) start value is repeated for all elements
1369 else if intEq(listLength(svals), 1) then stringDelimitList(List.fill(listHead(svals), n), " ")
1370 else stringDelimitList(svals, " ");
1371 else "";
1372 end match;
1373 end getFMI3ArrayStart;
1374
1375 public function getFMIArrayStartValues
1376 "Flattened list of the scalar start values of a (possibly array) start
1377 expression, in row major order. A scalar start expression yields a single
1378 value (broadcast by the caller)."
1379 input DAE.Exp e;
1380 output list<String> vals;
1381 algorithm
1382 vals := match e
1383 local DAE.Exp first; list<DAE.Exp> arr; Real r; Integer i; Boolean b; String s;
1384 275 case DAE.RCONST(r) then {realString(r)};
1385 54 case DAE.ICONST(i) then {intString(i)};
1386 ✗ case DAE.BCONST(b) then {if b then "true" else "false"};
1387 case DAE.SCONST(s) then {s};
1388
4/4
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 54 times.
✓ Branch 2 taken 162 times.
✓ Branch 3 taken 54 times.
216 case DAE.ARRAY(array = arr) then List.flatten(list(getFMIArrayStartValues(el) for el in arr));
1389 1 case DAE.REDUCTION(expr = first) then getFMIArrayStartValues(first);
1390 else {};
1391 end match;
1392 end getFMIArrayStartValues;
1393
1394 public function getFMIScalarVRs
1395 "Comma separated list of the scalar value references occupied by a (possibly
1396 array) FMI variable: base, base+1, ..., base+getNumElems-1. For a scalar this
1397 is just its value reference. Used for the STATES/STATESDERIVATIVES macros."
1398 input SimCodeVar.SimVar var;
1399 input SimCode.SimCode simCode;
1400 output String out;
1401 protected
1402 Integer base, n;
1403 list<String> refs = {};
1404 algorithm
1405 202 base := lookupVR(var.name, simCode);
1406 202 n := SimCodeUtilShared.getNumElems(var);
1407
1/2
✓ Branch 0 taken 202 times.
✗ Branch 1 not taken.
420 for i in 0:n-1 loop
1408 218 refs := String(base + i) :: refs;
1409 end for;
1410 202 out := stringDelimitList(listReverse(refs), ", ");
1411 end getFMIScalarVRs;
1412
1413 public function getScalarElements
1414 "Get scalar elements of an array in row major order. This is
1415 needed by templates for XML files that only support scalar variables.
1416 author: rfranke"
1417 input SimCodeVar.SimVar var;
1418 output list<SimCodeVar.SimVar> elts;
1419 protected
1420 list<Integer> dims;
1421 SimCodeVar.SimVar elt;
1422 Integer index;
1423 Integer fmi_index;
1424 algorithm
1425 // create list of elements
1426 elts := match var
1427 // check for exportVar = NONE() in type_ = T_ARRAY() which is filtered by default and should not be exported to modeldescription.xml in fmus
1428 case SimCodeVar.SIMVAR(type_=DAE.T_ARRAY(), exportVar = NONE()) then {};
1429
1430 case SimCodeVar.SIMVAR(type_=DAE.T_ARRAY(), variable_index=SOME(index), fmi_index=SOME(fmi_index)) algorithm
1431 56 dims := List.map(List.lastN(var.numArrayElement, listLength(var.numArrayElement)), stringInt);
1432 elt := var;
1433 56 elt.type_ := Types.arrayElementType(var.type_);
1434 56 elts := fillScalarElements(elt, dims, 1, {}, {});
1435 56 elts := setVariableIndexHelper(elts, index, fmi_index);
1436 then elts;
1437 else {var};
1438 end match;
1439 end getScalarElements;
1440
1441 protected function fillScalarElements
1442 "Helper for getScalarElements, called recursively for each dimension.
1443 author: rfranke"
1444 input SimCodeVar.SimVar eltIn;
1445 input list<Integer> dims;
1446 input Integer dimIdx;
1447 input list<DAE.Subscript> subsIn;
1448 input output list<SimCodeVar.SimVar> elts;
1449 protected
1450 SimCodeVar.SimVar elt = eltIn;
1451 list<DAE.Subscript> subs;
1452 algorithm
1453
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
484 for i in listGet(dims, dimIdx):-1:1 loop
1454 410 subs := DAE.INDEX(DAE.ICONST(i)) :: subsIn;
1455
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 392 times.
410 if dimIdx < listLength(dims) then
1456 18 elts := fillScalarElements(eltIn, dims, dimIdx + 1, subs, elts);
1457 else
1458 // add subscripts to array element
1459 392 subs := listReverse(subs);
1460 784 elt.name := ComponentReference.crefSetLastSubs(elt.name, subs);
1461 // copy the array subscripts to exportVar as it is used export vars in modeldescription.xml in CodegenFMUCommon.tpl
1462 elt.exportVar := SOME(ComponentReference.crefSetLastSubs(Util.getOption(elt.exportVar), subs));
1463 // add subscripts to previousName
1464 () := match elt
1465 local
1466 DAE.ComponentRef cref;
1467 Boolean fixed;
1468 case SimCodeVar.SIMVAR(varKind = BackendDAE.CLOCKED_STATE(previousName = cref, isStartFixed = fixed))
1469 algorithm
1470
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 30 times.
420 elt.varKind := BackendDAE.CLOCKED_STATE(ComponentReference.crefSetLastSubs(cref, subs), fixed);
1471 then ();
1472 else ();
1473 end match;
1474 elts := elt :: elts;
1475 end if;
1476 end for;
1477 end fillScalarElements;
1478
1479 public function getVariableIndex
1480 input SimCodeVar.SimVar inVar;
1481 output Integer outVariableIndex;
1482 algorithm
1483 outVariableIndex := match inVar
1484 local
1485 Integer variableIndex;
1486 case SimCodeVar.SIMVAR(variable_index = SOME(variableIndex))
1487 then variableIndex;
1488 else 0;
1489 end match;
1490 end getVariableIndex;
1491
1492 public function getVariableFMIIndex
1493 input SimCodeVar.SimVar inVar;
1494 output Integer outVariableIndex;
1495 algorithm
1496 outVariableIndex := match inVar
1497 local
1498 Integer variableIndex;
1499 case SimCodeVar.SIMVAR(fmi_index = SOME(variableIndex))
1500 then variableIndex;
1501 else 0;
1502 end match;
1503 end getVariableFMIIndex;
1504
1505 public function getValueReference
1506 "returns the value reference of a variable for direct memory access
1507 considering aliases and array storage order
1508 author: rfranke and mwalther and vwaurich and sjoelund"
1509 input SimCodeVar.SimVar inSimVar;
1510 input SimCode.SimCode inSimCode;
1511 input Boolean inElimNegAliases "=false to keep negative alias references";
1512 output String outValueReference;
1513 algorithm
1514 outValueReference := match (inSimVar, inElimNegAliases, Config.simCodeTarget())
1515 local
1516 SimCodeVar.SimVar simVar;
1517 DAE.ComponentRef cref;
1518 String valueReference;
1519 89 case (SimCodeVar.SIMVAR(aliasvar = SimCodeVar.NEGATEDALIAS(_)), false, _) then
1520 getDefaultValueReference(inSimVar, inSimCode.modelInfo.varInfo);
1521 case (_, _, _) guard stringEqual(Config.simCodeTarget(), "Cpp")
1522 algorithm
1523 // resolve aliases to get multi-dimensional arrays right
1524 // (this should possibly be done in getVarIndexByMapping?)
1525 simVar := match inSimVar
1526 local
1527 DAE.ComponentRef componentRef;
1528 case SimCodeVar.SIMVAR(aliasvar = SimCodeVar.ALIAS(varName = cref))
1529 3396 then cref2simvar(cref, inSimCode);
1530 case SimCodeVar.SIMVAR(aliasvar = SimCodeVar.NEGATEDALIAS(varName = cref))
1531 500 then cref2simvar(cref, inSimCode);
1532 // resolve pre vars
1533 case SimCodeVar.SIMVAR(name = DAE.CREF_QUAL(ident=DAE.preNamePrefix, componentRef=componentRef))
1534 ✗ then cref2simvar(componentRef, inSimCode);
1535 else inSimVar;
1536 end match;
1537
1538 12278 valueReference := getVarIndexByMapping(inSimCode.varToArrayIndexMapping, simVar.name, true, "-1");
1539
3/4
✓ Branch 0 taken 3623 times.
✓ Branch 1 taken 8655 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3623 times.
12278 if stringEqual(valueReference, "-1") then
1540 ✗ Error.addInternalError("invalid return value from getVarIndexByMapping for " + simVarString(simVar), sourceInfo());
1541 end if;
1542 then valueReference;
1543 88795 case (SimCodeVar.SIMVAR(aliasvar = SimCodeVar.ALIAS(varName = cref)), _, _) then
1544 getDefaultValueReference(cref2simvar(cref, inSimCode), inSimCode.modelInfo.varInfo);
1545 11743 else
1546 getDefaultValueReference(inSimVar, inSimCode.modelInfo.varInfo);
1547 end match;
1548 end getValueReference;
1549
1550 protected function getDefaultValueReference
1551 "returns the value reference without consideration of aliases,
1552 starting from zero for each base type
1553 author: rfranke"
1554 input SimCodeVar.SimVar inSimVar;
1555 input SimCode.VarInfo inVarInfo;
1556 output String outDefaultValueReference;
1557 protected
1558 Integer reference;
1559 Integer numReal = 2*inVarInfo.numStateVars + inVarInfo.numAlgVars + inVarInfo.numDiscreteReal + inVarInfo.numParams + inVarInfo.numAlgAliasVars;
1560 Integer numInteger = inVarInfo.numIntAlgVars + inVarInfo.numIntParams + inVarInfo.numIntAliasVars;
1561 Integer numBoolean = inVarInfo.numBoolAlgVars + inVarInfo.numBoolParams + inVarInfo.numBoolAliasVars;
1562 algorithm
1563 100627 reference := getVariableIndex(inSimVar);
1564
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 100594 times.
100627 if reference > numReal + numInteger + numBoolean then
1565 // String variable
1566 33 reference := reference - numReal - numInteger - numBoolean;
1567 elseif reference > numReal + numInteger then
1568 // Boolean variable
1569 1455 reference := reference - numReal - numInteger;
1570 elseif reference > numReal then
1571 // Integer variable
1572 2964 reference := reference - numReal;
1573 elseif reference < 0 then
1574 ✗ Error.addInternalError("invalid return value from getVariableIndex", sourceInfo());
1575 end if;
1576 100627 outDefaultValueReference := String(reference - 1);
1577 end getDefaultValueReference;
1578
1579 public function getFMI3TypeOffset
1580 "Returns the offset that is added to the per-base-type value reference to make
1581 it globally unique, as required by the FMI 3.0 standard (in FMI 2.0 value
1582 references only need to be unique per base type). The offsets are chosen so
1583 that they match the per-base-type array layout used by getDefaultValueReference
1584 and the C runtime (fmu3_model_interface.c): reals first, then integers, then
1585 booleans, then strings. The very same offsets are emitted as #defines into the
1586 generated FMI 3.0 model code so the runtime can recover the per-type index by
1587 subtracting the offset.
1588 author: adrpo"
1589 input DAE.Type inType;
1590 input SimCode.ModelInfo inModelInfo;
1591 output Integer outOffset;
1592 protected
1593 // Per-scalar counts from varInfo (O(1)). Re-counting the variable lists here on
1594 // every call is O(n) and this runs once per variable, i.e. O(n^2) overall.
1595 SimCode.VarInfo vi = inModelInfo.varInfo;
1596 Integer numReal = 2*vi.numStateVars + vi.numAlgVars + vi.numDiscreteReal + vi.numParams + vi.numAlgAliasVars;
1597 Integer numInteger = vi.numIntAlgVars + vi.numIntParams + vi.numIntAliasVars;
1598 Integer numBoolean = vi.numBoolAlgVars + vi.numBoolParams + vi.numBoolAliasVars;
1599 Integer numString = vi.numStringAlgVars + vi.numStringParamVars + vi.numStringAliasVars;
1600 algorithm
1601 outOffset := match inType
1602 local DAE.Type aty;
1603 case DAE.T_REAL() then 0;
1604 // enumerations are stored in the integer arrays of the OM runtime
1605 case DAE.T_INTEGER() then numReal;
1606 case DAE.T_ENUMERATION() then numReal;
1607 321 case DAE.T_BOOL() then numReal + numInteger;
1608 36 case DAE.T_STRING() then numReal + numInteger + numBoolean;
1609 // external objects are exported as FMI 3.0 Binary, after the string block
1610 1 case DAE.T_COMPLEX(complexClassType = ClassInf.EXTERNAL_OBJ()) then numReal + numInteger + numBoolean + numString;
1611 // non-scalarized array variable: the offset is determined by the element type
1612 433 case DAE.T_ARRAY(ty = aty) then getFMI3TypeOffset(aty, inModelInfo);
1613 else 0;
1614 end match;
1615 end getFMI3TypeOffset;
1616
1617 public function getFMI2ValueReferenceOffsets
1618 "The offsets that turn an FMI 2.0 value reference, which is unique only per base
1619 type, into the globally unique FMI 3.0 one, in the order Real, Integer, Boolean,
1620 String. The wasm FMU export ships them with the FMU so its loader can serve the
1621 FMI 2.0 API from a component that speaks FMI 3.0."
1622 input SimCode.ModelInfo modelInfo;
1623 output list<Integer> offsets;
1624 algorithm
1625 ✗ offsets := {getFMI3TypeOffset(DAE.T_REAL_DEFAULT, modelInfo),
1626 getFMI3TypeOffset(DAE.T_INTEGER_DEFAULT, modelInfo),
1627 getFMI3TypeOffset(DAE.T_BOOL_DEFAULT, modelInfo),
1628 getFMI3TypeOffset(DAE.T_STRING_DEFAULT, modelInfo)};
1629 end getFMI2ValueReferenceOffsets;
1630
1631 public function getFMI3ValueReference
1632 "Returns the globally unique value reference of a variable for the FMI 3.0
1633 export. It is the per-base-type value reference (see getValueReference) shifted
1634 by the per-base-type offset (see getFMI3TypeOffset).
1635 author: adrpo"
1636 input SimCodeVar.SimVar inSimVar;
1637 input SimCode.SimCode inSimCode;
1638 output String outValueReference;
1639 protected
1640 Integer offset, localRef;
1641 algorithm
1642 3498 offset := getFMI3TypeOffset(inSimVar.type_, inSimCode.modelInfo);
1643 // Use the element-cumulative per-base-type value-reference map (same one the
1644 // C runtime macros use via lookupVR) so that array variables get the value
1645 // reference of their first scalar element and occupy a contiguous block.
1646 // For scalars this equals the former getValueReference result.
1647 3498 localRef := lookupVR(inSimVar.name, inSimCode);
1648 3498 outValueReference := String(offset + localRef);
1649 end getFMI3ValueReference;
1650
1651 protected function fmi3ModelVariableLists
1652 "The variable lists in the order the FMI indices were handed out."
1653 input SimCodeVar.SimVars vars;
1654 output list<list<SimCodeVar.SimVar>> allLists;
1655 algorithm
1656 17 allLists := {vars.stateVars, vars.derivativeVars, vars.algVars, vars.discreteAlgVars,
1657 vars.intAlgVars, vars.boolAlgVars, vars.stringAlgVars,
1658 vars.inputVars, vars.outputVars,
1659 vars.paramVars, vars.intParamVars, vars.boolParamVars, vars.stringParamVars,
1660 vars.aliasVars, vars.intAliasVars, vars.boolAliasVars, vars.stringAliasVars};
1661 end fmi3ModelVariableLists;
1662
1663 public function cacheFMI3ValueReferences
1664 "Build the FMI index -> value reference table the <ModelStructure> emitter reads,
1665 and keep it for as long as one is being written (see clearFMI3ValueReferences).
1666
1667 Without it every unknown and every one of its dependencies searches all
1668 seventeen variable lists for its index, which on a model with thousands of
1669 variables is most of what exporting an FMI 3.0 FMU costs: FullRobot spent 15 of
1670 its 33 export seconds in that search."
1671 input SimCode.SimCode simCode;
1672 // Susan calls this for its effect; the empty string is what it interpolates.
1673 output String dummy = "";
1674 protected
1675 list<list<SimCodeVar.SimVar>> allLists = fmi3ModelVariableLists(simCode.modelInfo.vars);
1676 array<String> table;
1677 Integer n = 0, i;
1678 algorithm
1679
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 16 times.
288 for lst in allLists loop
1680
2/2
✓ Branch 0 taken 3359 times.
✓ Branch 1 taken 272 times.
3631 for v in lst loop
1681 3359 n := intMax(n, getVariableFMIIndex(v));
1682 end for;
1683 end for;
1684 // Index 0 is no variable's, so an unmapped entry keeps its own number as the
1685 // uncached lookup did.
1686 16 table := arrayCreate(n, "");
1687
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 16 times.
288 for lst in allLists loop
1688
2/2
✓ Branch 0 taken 3359 times.
✓ Branch 1 taken 272 times.
3631 for v in lst loop
1689 3359 i := getVariableFMIIndex(v);
1690
4/4
✓ Branch 0 taken 2035 times.
✓ Branch 1 taken 1324 times.
✓ Branch 3 taken 2034 times.
✓ Branch 4 taken 1 time.
3359 if i > 0 and i <= n and stringEmpty(arrayGet(table, i)) then
1691 2034 arrayUpdate(table, i, getFMI3ValueReference(v, simCode));
1692 end if;
1693 end for;
1694 end for;
1695 16 setGlobalRoot(Global.fmi3ValueReferenceCache, SOME(table));
1696 end cacheFMI3ValueReferences;
1697
1698 public function clearFMI3ValueReferences
1699 "Drop what cacheFMI3ValueReferences built, so the next model builds its own."
1700 output String dummy = "";
1701 algorithm
1702 16 setGlobalRoot(Global.fmi3ValueReferenceCache, NONE());
1703 end clearFMI3ValueReferences;
1704
1705 public function fmi3UnknownDependencyAttributes
1706 "The dependencies and dependenciesKind attributes of a <ModelStructure> entry,
1707 the dependencies mapped from FMI indices to value references."
1708 input SimCode.SimCode simCode;
1709 input SimCode.FmiUnknown unknown;
1710 output String attributes = "";
1711 protected
1712 Option<array<String>> cache = getGlobalRoot(Global.fmi3ValueReferenceCache);
1713 list<String> vrs = {};
1714 algorithm
1715
2/2
✓ Branch 0 taken 203 times.
✓ Branch 1 taken 307 times.
510 if not listEmpty(unknown.dependencies) then
1716
2/2
✓ Branch 0 taken 360 times.
✓ Branch 1 taken 203 times.
563 for d in unknown.dependencies loop
1717 vrs := match cache
1718 local array<String> table;
1719 case SOME(table) guard d > 0 and d <= arrayLength(table) and not stringEmpty(arrayGet(table, d))
1720 then arrayGet(table, d) :: vrs;
1721 ✗ else getFMI3ValueReferenceFromFMIIndex(simCode, d) :: vrs;
1722 end match;
1723 end for;
1724 203 attributes := " dependencies=\"" + stringDelimitList(listReverse(vrs), " ") + "\"";
1725 end if;
1726
2/2
✓ Branch 0 taken 307 times.
✓ Branch 1 taken 203 times.
510 if not listEmpty(unknown.dependenciesKind) then
1727 203 attributes := attributes + " dependenciesKind=\"" + stringDelimitList(unknown.dependenciesKind, " ") + "\"";
1728 end if;
1729 end fmi3UnknownDependencyAttributes;
1730
1731 public function fmiDependenciesString
1732 "Space separated, as the FMI 2.0 ModelStructure dependencies attribute."
1733 input list<Integer> dependencies;
1734 output String str;
1735 algorithm
1736
4/4
✓ Branch 0 taken 645 times.
✓ Branch 1 taken 821 times.
✓ Branch 2 taken 645 times.
✓ Branch 3 taken 821 times.
1466 str := stringDelimitList(list(intString(d) for d in dependencies), " ");
1737 end fmiDependenciesString;
1738
1739 public function fmiDependenciesKindString
1740 input list<String> kinds;
1741 output String str;
1742 algorithm
1743 821 str := stringDelimitList(kinds, " ");
1744 end fmiDependenciesKindString;
1745
1746 public function getFMI3ValueReferenceFromFMIIndex
1747 "Maps an FMI variable index (the 1-based position in the ModelVariables list as
1748 stored in the FmiModelStructure unknowns/dependencies) to the globally unique
1749 FMI 3.0 value reference of the corresponding variable. Used to emit the
1750 ModelStructure (Output/ContinuousStateDerivative/InitialUnknown) which, unlike
1751 FMI 2.0, references variables by valueReference instead of by index.
1752 Returns the input index as a string if no matching variable is found, so the
1753 generated XML is still well-formed.
1754 author: adrpo"
1755 input SimCode.SimCode inSimCode;
1756 input Integer inFMIIndex;
1757 output String outValueReference;
1758 protected
1759 SimCodeVar.SimVars vars = inSimCode.modelInfo.vars;
1760 Option<array<String>> cache;
1761 array<String> table;
1762 Option<SimCodeVar.SimVar> found = NONE();
1763 algorithm
1764 510 cache := getGlobalRoot(Global.fmi3ValueReferenceCache);
1765
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 510 times.
✓ Branch 2 taken 510 times.
✗ Branch 3 not taken.
510 if isSome(cache) then
1766 510 SOME(table) := cache;
1767
4/6
✓ Branch 0 taken 509 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 509 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 509 times.
✗ Branch 6 not taken.
1019 if inFMIIndex > 0 and inFMIIndex <= arrayLength(table) and not stringEmpty(arrayGet(table, inFMIIndex)) then
1768 outValueReference := arrayGet(table, inFMIIndex);
1769 509 return;
1770 end if;
1771 end if;
1772
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 for lst in fmi3ModelVariableLists(vars) loop
1773
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 for v in lst loop
1774
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 if intEq(getVariableFMIIndex(v), inFMIIndex) then
1775 found := SOME(v);
1776 1 break;
1777 end if;
1778 end for;
1779
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 if isSome(found) then
1780 break;
1781 end if;
1782 end for;
1783 outValueReference := match found
1784 1 case SOME(_) then getFMI3ValueReference(Util.getOption(found), inSimCode);
1785 ✗ else String(inFMIIndex);
1786 end match;
1787 end getFMI3ValueReferenceFromFMIIndex;
1788
1789 public function getFMI3TimeValueReference
1790 "Returns a value reference for the independent variable (time) that does not
1791 collide with any model variable. It is the first free value reference past the
1792 real/integer/boolean/string/binary/clock blocks. The same value is emitted as a
1793 #define (FMI3_TIME_VR) into the generated FMI 3.0 model code.
1794 author: adrpo"
1795 input SimCode.SimCode inSimCode;
1796 output String outValueReference;
1797 algorithm
1798 48 outValueReference := String(getFMI3ClockVROffset(inSimCode.modelInfo) + listLength(inSimCode.clockedPartitions));
1799 end getFMI3TimeValueReference;
1800
1801 public constant String FMI_LS_DAE_VERSION = "1.0.0-alpha.1";
1802
1803 public function fmiLsDaeVersion
1804 "The version of fmi-ls-dae the manifest declares. FMI_LS_DAE_DRAFT_DATE and
1805 FMI_LS_DAE_DRAFT_COMMIT name the revision of github.com/modelica/fmi-ls-dae
1806 the export was written against, which the export reports since the layered
1807 standard is still a draft."
1808 output String version = FMI_LS_DAE_VERSION;
1809 end fmiLsDaeVersion;
1810
1811 public function getFMI3DaeModeValueReference
1812 "fmi-ls-dae: the value reference of the structural parameter that switches a
1813 --daeMode FMU into DAE mode, the first one past the event indicators. The
1814 residuals follow it (fmi3DaeResiduals); the wasm emitter
1815 (CodegenWasmJit.build_fmi_vrs) assigns the same numbers."
1816 input SimCode.SimCode simCode;
1817 output String vr;
1818 algorithm
1819 ✗ vr := String(stringInt(getFMI3TimeValueReference(simCode)) + simCode.modelInfo.varInfo.numZeroCrossings + 1);
1820 end getFMI3DaeModeValueReference;
1821
1822 public function fmi3DaeResiduals
1823 "fmi-ls-dae: the residuals of a --daeMode model as (valueReference, dependency
1824 attributes): the value references follow the DAE-mode switch's, and the
1825 dependencies and dependenciesKind attributes of each <Residual> are read
1826 off the rows of the DAE-mode Jacobian's transposed sparsity. A state column
1827 stands for the state and its derivative both, since DAE-mode differentiation
1828 folds der(x) into x ($cj * x.Seed); the other columns are the algebraic
1829 variables. No attributes without a pattern, which the standard reads as a
1830 dependency on every known."
1831 input SimCode.SimCode simCode;
1832 output list<tuple<String, String>> residuals = {};
1833 protected
1834 SimCode.DaeModeData dmd;
1835 Option<list<list<Integer>>> rows;
1836 list<list<Integer>> rest;
1837 list<Integer> cols;
1838 array<String> stateVRs, algebraicVRs;
1839 Integer numStates, daeModeVR;
1840 String vr, attributes;
1841 list<String> acc;
1842 algorithm
1843 ✗ SOME(dmd) := simCode.daeModeData;
1844 ✗ daeModeVR := stringInt(getFMI3DaeModeValueReference(simCode));
1845 rows := match dmd.sparsityPattern
1846 local SimCode.JacobianMatrix jm;
1847 ✗ case SOME(jm) then SOME(list(Util.tuple22(e) for e in jm.sparsityT));
1848 else NONE();
1849 end match;
1850 ✗ numStates := numScalarElems(simCode.modelInfo.vars.stateVars);
1851 ✗ stateVRs := listArray(list(getFMI3ValueReference(v, simCode) for v in simCode.modelInfo.vars.stateVars));
1852 ✗ algebraicVRs := listArray(list(getFMI3ValueReference(v, simCode) for v in dmd.algebraicVars));
1853 ✗ for var in dmd.residualVars loop
1854 attributes := "";
1855 ✗ if isSome(rows) then
1856 ✗ SOME(rest) := rows;
1857 ✗ if listEmpty(rest) then
1858 cols := {};
1859 else
1860 ✗ cols := listHead(rest);
1861 ✗ rows := SOME(listRest(rest));
1862 end if;
1863 acc := {};
1864 ✗ for c in cols loop
1865 ✗ if c < numStates then
1866 ✗ vr := arrayGet(stateVRs, c + 1);
1867 ✗ acc := String(stringInt(vr) + numStates) :: vr :: acc;
1868 else
1869 ✗ acc := arrayGet(algebraicVRs, c - numStates + 1) :: acc;
1870 end if;
1871 end for;
1872 ✗ acc := listReverse(acc);
1873 ✗ attributes := " dependencies=\"" + stringDelimitList(acc, " ") + "\" dependenciesKind=\""
1874 + stringDelimitList(list("dependent" for s in acc), " ") + "\"";
1875 end if;
1876 ✗ residuals := (String(daeModeVR + 1 + var.index), attributes) :: residuals;
1877 end for;
1878 ✗ residuals := listReverse(residuals);
1879 end fmi3DaeResiduals;
1880
1881 protected function getNLSysRHS
1882 input list<SimCode.SimEqSystem> eqs;
1883 input list<DAE.ComponentRef> res ;
1884 output list<DAE.ComponentRef> unknowns;
1885 algorithm
1886 unknowns := matchcontinue (eqs,res)
1887 local list<SimCode.SimEqSystem> tail;
1888 DAE.Exp exp;
1889 case ({},_)
1890 then res;
1891 case (SimCode.SES_RESIDUAL(exp=exp) :: tail,_)
1892 ✗ then getNLSysRHS(tail,listAppend(res,Expression.getAllCrefs(exp)));
1893 case (SimCode.SES_FOR_RESIDUAL(exp=exp) :: tail,_)
1894 ✗ then getNLSysRHS(tail,listAppend(res,Expression.getAllCrefs(exp))); // strip crefs?
1895 case (SimCode.SES_GENERIC_RESIDUAL(exp=exp) :: tail,_)
1896 ✗ then getNLSysRHS(tail,listAppend(res,Expression.getAllCrefs(exp))); // strip crefs?
1897 case (_,)
1898 algorithm
1899 ✗ print("getNLSysRHS failed\n");
1900 ✗ then
1901 fail();
1902 end matchcontinue;
1903 end getNLSysRHS;
1904
1905 protected function computeDependenciesHelper
1906 input list<SimCode.SimEqSystem> eqs;
1907 input list<DAE.ComponentRef> unknowns;
1908 input list<SimCode.SimEqSystem> res;
1909 output list<SimCode.SimEqSystem> deps;
1910 algorithm
1911 deps := matchcontinue (eqs, res)
1912 local list<SimCode.SimEqSystem> tail;
1913 SimCode.SimEqSystem head;
1914 list<DAE.ComponentRef> new_unknowns;
1915 list<SimCode.SimEqSystem> r;
1916 DAE.ComponentRef cref;
1917 list<DAE.ComponentRef> linsys_unk;
1918 list<DAE.ComponentRef> nlsys_unk;
1919 list<SimCode.SimEqSystem> nlsys_eqs;
1920 DAE.Exp exp;
1921 list<DAE.Exp> beqs;
1922 case ({}, r)
1923 then r;
1924 case ((head as SimCode.SES_SIMPLE_ASSIGN(cref=cref,exp=exp))::tail, r)
1925 algorithm
1926 ✗ true := List.isMemberOnTrue(cref,unknowns,ComponentReferenceBasics.crefEqual);
1927 // We must include this equation in the ODE
1928 ✗ new_unknowns := Expression.getAllCrefs(exp);
1929 // And include all those one defining the RHS
1930 ✗ then computeDependenciesHelper(tail,listAppend(unknowns,new_unknowns), listAppend(r,{head}));
1931 case ((head as SimCode.SES_SIMPLE_ASSIGN_CONSTRAINTS(cref=cref,exp=exp))::tail, r)
1932 algorithm
1933 ✗ true := List.isMemberOnTrue(cref,unknowns,ComponentReferenceBasics.crefEqual);
1934 // We must include this equation in the ODE
1935 ✗ new_unknowns := Expression.getAllCrefs(exp);
1936 // And include all those one defining the RHS
1937 ✗ then computeDependenciesHelper(tail,listAppend(unknowns,new_unknowns), listAppend(r,{head}));
1938 case ((head as SimCode.SES_LINEAR(lSystem = SimCode.LINEARSYSTEM( beqs=beqs)))::tail, r)
1939 algorithm
1940 // This linear system defines the following crefs
1941 ✗ linsys_unk := getSimEqSystemCrefsLHS(head);
1942 // If any of those are in our unkowns me must include this equation system
1943 ✗ false := listEmpty(List.intersectionOnTrue(linsys_unk,unknowns,ComponentReferenceBasics.crefEqual));
1944 // And include all the variables of the RHS to the unkowns
1945 ✗ new_unknowns := List.flatten(List.map(beqs, Expression.getAllCrefs));
1946 ✗ then computeDependenciesHelper(tail,listAppend(unknowns,new_unknowns),listAppend(r,{head}));
1947 case ((head as SimCode.SES_NONLINEAR(nlSystem=SimCode.NONLINEARSYSTEM(crefs=nlsys_unk, eqs=nlsys_eqs)))::tail, r)
1948 algorithm
1949 // If any of the uknwonw of the NL system are in our unkowns me must include this equation system
1950 ✗ false := listEmpty(List.intersectionOnTrue(nlsys_unk,unknowns,ComponentReferenceBasics.crefEqual));
1951 ✗ new_unknowns := getNLSysRHS(nlsys_eqs,{});
1952 ✗ then computeDependenciesHelper(tail,listAppend(unknowns,new_unknowns),listAppend(r,{head}));
1953 case (_::tail, r)
1954 ✗ then computeDependenciesHelper(tail,unknowns,r);
1955 end matchcontinue;
1956 end computeDependenciesHelper;
1957
1958 public function computeDependencies
1959 input list<SimCode.SimEqSystem> eqs;
1960 input DAE.ComponentRef cref;
1961 output list<SimCode.SimEqSystem> deps;
1962 algorithm
1963 ✗ deps := match cref
1964 case _
1965 then listReverse(computeDependenciesHelper(listReverse(eqs),{cref},{}));
1966 end match;
1967 end computeDependencies;
1968
1969 public function getSimEqSystemsByIndexLst
1970 input list<Integer> idcs;
1971 input list<SimCode.SimEqSystem> allSes;
1972 output list<SimCode.SimEqSystem> sesOut;
1973 algorithm
1974 ✗ sesOut := List.map1(idcs,getSimEqSysForIndex,allSes);
1975 end getSimEqSystemsByIndexLst;
1976
1977 public function getInputIndex
1978 input SimCodeVar.SimVar var;
1979 output Integer inputIndex;
1980 protected
1981 array<Integer> v;
1982 algorithm
1983 inputIndex := match var
1984 248 case SimCodeVar.SIMVAR(inputIndex=SOME(v)) guard arrayLength(v)==1 then arrayGet(v, 1);
1985 case SimCodeVar.SIMVAR(inputIndex=SOME(_))
1986 algorithm
1987 ✗ Error.addInternalError("Failed to SimCodeUtil.getInputIndex of variable", sourceInfo());
1988 ✗ then fail();
1989 else -1;
1990 end match;
1991 end getInputIndex;
1992
1993 public function resetFunctionIndex
1994 algorithm
1995 30377 setGlobalRoot(Global.codegenFunctionList, DoubleEnded.fromList({}));
1996 end resetFunctionIndex;
1997
1998 public function addFunctionIndex
1999 input String prefix, suffix;
2000 output String newName;
2001 protected
2002 DoubleEnded.MutableList<String> delst;
2003 algorithm
2004 335 delst := getGlobalRoot(Global.codegenFunctionList);
2005 335 newName := prefix + String(DoubleEnded.length(delst)) + suffix;
2006 335 DoubleEnded.push_back(delst, newName);
2007 end addFunctionIndex;
2008
2009 public function nVariablesReal
2010 input SimCode.VarInfo varInfo;
2011 output Integer n;
2012 algorithm
2013 1204 n := 2*varInfo.numStateVars+varInfo.numAlgVars+varInfo.numDiscreteReal+varInfo.numOptimizeConstraints+varInfo.numOptimizeFinalConstraints;
2014 end nVariablesReal;
2015
2016 public function getSimCode
2017 output SimCode.SimCode code;
2018 protected
2019 Option<SimCode.SimCode> ocode;
2020 algorithm
2021 2788909 ocode := getGlobalRoot(Global.optionSimCode);
2022 code := match ocode
2023 local SimCode.SimCode c;
2024 case SOME(c) then c;
2025 ✗ else algorithm Error.addInternalError("Tried to generate code that requires the SimCode structure, but this is not set (function context?)", sourceInfo()); then fail();
2026 end match;
2027 end getSimCode;
2028
2029 public function timeEventTrigger
2030 "The e of a relation `time >= e` or `time < e` (or `e <= time`, `e > time`)
2031 where e changes only at events. Such a relation switches exactly when time
2032 reaches e, which the runtime schedules as a time event."
2033 input DAE.Exp rel;
2034 output Option<DAE.Exp> trigger = NONE();
2035 protected
2036 SimCode.SimCode simCode;
2037 algorithm
2038
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19622 times.
19622 if not isSimulationCodegen() then
2039 ✗ return;
2040 end if;
2041 19622 simCode := getSimCode();
2042 trigger := match rel
2043 case DAE.RELATION(exp1 = DAE.CREF(componentRef = DAE.CREF_IDENT(ident = "time")), optionExpisASUB = NONE())
2044 guard rel.index >= 0 and (match rel.operator case DAE.GREATEREQ() then true; case DAE.LESS() then true; else false; end match)
2045 and isEventConstantExp(rel.exp2, simCode)
2046 8019 then SOME(rel.exp2);
2047 case DAE.RELATION(exp2 = DAE.CREF(componentRef = DAE.CREF_IDENT(ident = "time")), optionExpisASUB = NONE())
2048 guard rel.index >= 0 and (match rel.operator case DAE.LESSEQ() then true; case DAE.GREATER() then true; else false; end match)
2049 and isEventConstantExp(rel.exp1, simCode)
2050 ✗ then SOME(rel.exp1);
2051 else NONE();
2052 end match;
2053 end timeEventTrigger;
2054
2055 public function isTimeEventRelation
2056 input DAE.Exp rel;
2057 output Boolean b = isSome(timeEventTrigger(rel));
2058 end isTimeEventRelation;
2059
2060 protected function isTimeIndependentVar
2061 input DAE.ComponentRef cref;
2062 output Boolean b;
2063 protected
2064 Option<UnorderedSet<DAE.ComponentRef>> vars = getGlobalRoot(Global.timeIndependentVars);
2065 algorithm
2066 b := match vars
2067 local
2068 UnorderedSet<DAE.ComponentRef> s;
2069 540 case SOME(s) then UnorderedSet.contains(cref, s);
2070 else false;
2071 end match;
2072 end isTimeIndependentVar;
2073
2074 protected function isEventConstantExp
2075 "Whether exp can only change at events."
2076 input DAE.Exp exp;
2077 input SimCode.SimCode simCode;
2078 output Boolean b;
2079 protected
2080 SimCodeVar.SimVar v;
2081 algorithm
2082 b := match exp
2083 case DAE.ICONST() then true;
2084 case DAE.RCONST() then true;
2085 case DAE.BCONST() then true;
2086 case DAE.ENUM_LITERAL() then true;
2087 case DAE.CREF()
2088 guard not ComponentReference.isTime(exp.componentRef)
2089 algorithm
2090 10091 v := cref2simvar(exp.componentRef, simCode);
2091 then v.index <> -2 and (not Types.isRealOrSubTypeReal(exp.ty) or
2092 (match v.varKind
2093 case BackendDAE.DISCRETE() then true;
2094 case BackendDAE.PARAM() then true;
2095 case BackendDAE.CONST() then true;
2096 687 else isTimeIndependentVar(exp.componentRef);
2097 end match));
2098 case DAE.CALL(path = Absyn.IDENT("pre"), expLst = {DAE.CREF()}) then true;
2099
4/4
✓ Branch 1 taken 2484 times.
✓ Branch 2 taken 22 times.
✓ Branch 4 taken 99 times.
✓ Branch 5 taken 2385 times.
2506 case DAE.BINARY() then isEventConstantExp(exp.exp1, simCode) and isEventConstantExp(exp.exp2, simCode);
2100 ✗ case DAE.UNARY() then isEventConstantExp(exp.exp, simCode);
2101 99 case DAE.CAST() then isEventConstantExp(exp.exp, simCode);
2102 else false;
2103 end match;
2104 end isEventConstantExp;
2105
2106 public function isSimulationCodegen
2107 "Whether the templates are running for a simulation or an FMU rather than for
2108 functions on their own. Only those set the SimCode structure, and only those
2109 compile against the counted runtime, so it also answers which of the two
2110 vocabularies the generated C is written in."
2111 output Boolean simulation;
2112 protected
2113 Option<SimCode.SimCode> ocode;
2114 algorithm
2115 1773971 ocode := getGlobalRoot(Global.optionSimCode);
2116 simulation := match ocode case SOME(_) then true; else false; end match;
2117 end isSimulationCodegen;
2118
2119 public function isContiguousArrayCref
2120 "Whether the scalarized elements of an array cref occupy consecutive slots of
2121 one variable array, so the C target may address them through the first one.
2122 A Jacobian's own variables only if its table has the array itself, as the
2123 new backend's does."
2124 input DAE.ComponentRef inCref;
2125 input SimCodeFunction.Context context;
2126 output Boolean outContiguous = true;
2127 protected
2128 SimCode.SimCode simCode = getSimCode();
2129 SimCodeVar.SimVar v;
2130 Integer next = -1;
2131 Boolean param, firstParam = false, jacVar;
2132 list<DAE.ComponentRef> crefs;
2133 algorithm
2134
2/2
✓ Branch 0 taken 292 times.
✓ Branch 1 taken 2910 times.
3202 if not simCode.scalarized then
2135 292 return;
2136 end if;
2137 2910 crefs := ComponentReference.expandCref(inCref, true);
2138 (jacVar, outContiguous) := match (context, crefs)
2139 local
2140 HashTableCrefSimVar.HashTable jacHT;
2141 DAE.ComponentRef cr;
2142 case (SimCodeFunction.JACOBIAN_CONTEXT(jacHT = SOME(jacHT)), cr :: _)
2143 guard isJacobianColumnCref(cr) or List.any(crefs, function BaseHashTable.hasKey(hashTable = jacHT))
2144 38 then (true, BaseHashTable.hasKey(ComponentReference.crefStripSubs(inCref), jacHT));
2145 else (false, true);
2146 end match;
2147 if jacVar then
2148 38 return;
2149 end if;
2150
2/2
✓ Branch 0 taken 24086 times.
✓ Branch 1 taken 2825 times.
26911 for cr in crefs loop
2151 24086 v := cref2simvar(cr, simCode);
2152 // A cref the SimCode does not know is a whole-array Jacobian seed,
2153 // addressed through the seed's own value array.
2154
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 24040 times.
24086 if v.index < 0 then
2155 46 return;
2156 end if;
2157 // Parameters live in their own value array (`varArrayName`).
2158 param := match v.varKind case BackendDAE.PARAM() then true; else false; end match;
2159
2/2
✓ Branch 0 taken 2826 times.
✓ Branch 1 taken 21214 times.
24040 if next == -1 then
2160 firstParam := param;
2161 end if;
2162 outContiguous := match v.aliasvar
2163
5/6
✓ Branch 0 taken 24039 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2826 times.
✓ Branch 3 taken 21213 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 21213 times.
24040 case SimCodeVar.NOALIAS() then param == firstParam and (next == -1 or v.index == next);
2164 else false;
2165 end match;
2166 if not outContiguous then
2167 1 return;
2168 end if;
2169 24039 next := v.index + 1;
2170 end for;
2171 end isContiguousArrayCref;
2172
2173 public function contiguousSliceStart
2174 "The subscripts of the first element of a slice that is one block of its
2175 variable's storage, {} for any other slice. Such a slice has scalar
2176 subscripts, then optionally one literal range with step 1, then whole
2177 dimensions, over constant dimensions."
2178 input list<DAE.Subscript> subs;
2179 input list<DAE.Dimension> dims;
2180 output list<DAE.Subscript> start;
2181 algorithm
2182 12 (start, _) := contiguousSlice(subs, dims);
2183 end contiguousSliceStart;
2184
2185 public function contiguousSliceDims
2186 "The dimensions of a slice accepted by contiguousSliceStart."
2187 input list<DAE.Subscript> subs;
2188 input list<DAE.Dimension> dims;
2189 output list<Integer> sliceDims;
2190 algorithm
2191 309 (_, sliceDims) := contiguousSlice(subs, dims);
2192 end contiguousSliceDims;
2193
2194 protected function contiguousSlice
2195 input list<DAE.Subscript> subs;
2196 input list<DAE.Dimension> dims;
2197 output list<DAE.Subscript> start = {};
2198 output list<Integer> sliceDims = {};
2199 protected
2200 Integer i, j, d;
2201 Option<DAE.Exp> step;
2202 DAE.Exp e;
2203 DAE.Subscript sub;
2204 list<DAE.Subscript> rest = subs;
2205 Boolean inBlock = false, sliced = false, ok;
2206 algorithm
2207
2/2
✓ Branch 0 taken 564 times.
✓ Branch 1 taken 62 times.
626 for dim in dims loop
2208 551 d := match dim case DAE.DIM_INTEGER() then dim.integer; else -1; end match;
2209
1/2
✓ Branch 0 taken 564 times.
✗ Branch 1 not taken.
564 if listEmpty(rest) then
2210 sub := DAE.WHOLEDIM();
2211 else
2212 564 sub :: rest := rest;
2213 end if;
2214
2/2
✓ Branch 0 taken 551 times.
✓ Branch 1 taken 13 times.
564 ok := if d < 1 then false else match sub
2215 case DAE.INDEX(exp = DAE.ICONST(integer = i)) guard not inBlock and i >= 1 and i <= d
2216 algorithm
2217 start := sub :: start;
2218 then true;
2219 case DAE.INDEX(exp = e) guard not inBlock and not Expression.isConst(e) and Types.isInteger(Expression.typeof(e))
2220 algorithm
2221 start := sub :: start;
2222 then true;
2223 case DAE.SLICE(exp = DAE.RANGE(start = DAE.ICONST(integer = i), step = step, stop = DAE.ICONST(integer = j)))
2224 guard not inBlock and i >= 1 and j >= i and j <= d and Util.applyOptionOrDefault(step, Expression.isConstOne, true)
2225 algorithm
2226 inBlock := true;
2227 sliced := true;
2228 24 start := DAE.INDEX(DAE.ICONST(i)) :: start;
2229 24 sliceDims := (j - i + 1) :: sliceDims;
2230 then true;
2231 case DAE.WHOLEDIM()
2232 algorithm
2233 inBlock := true;
2234 start := DAE.INDEX(DAE.ICONST(1)) :: start;
2235 sliceDims := d :: sliceDims;
2236 then true;
2237 else false;
2238 end match;
2239
2/2
✓ Branch 0 taken 259 times.
✓ Branch 1 taken 305 times.
564 if not ok then
2240 start := {};
2241 sliceDims := {};
2242 259 return;
2243 end if;
2244 305 sliced := sliced or not inBlock;
2245 end for;
2246
4/6
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 46 times.
✗ Branch 5 not taken.
62 if not listEmpty(rest) or not sliced or listEmpty(sliceDims) then
2247 start := {};
2248 sliceDims := {};
2249 else
2250 46 start := listReverse(start);
2251 46 sliceDims := listReverse(sliceDims);
2252 end if;
2253 end contiguousSlice;
2254
2255 public function stackArrayLength
2256 "The number of elements of a function's array variable that can be stored on
2257 the stack: not an output, not bound from outside or to a shared literal,
2258 Real, Integer or Boolean elements, constant dimensions and at most 256
2259 elements. 0 for any other variable."
2260 input SimCodeFunction.Variable var;
2261 input SimCodeFunction.Function fn;
2262 output Integer n = 0;
2263 protected
2264 72896 list<SimCodeFunction.Variable> outVars = match fn case SimCodeFunction.FUNCTION() then fn.outVars; else {}; end match;
2265 algorithm
2266 _ := match var
2267 case SimCodeFunction.VARIABLE(parallelism = DAE.NON_PARALLEL(), bind_from_outside = false)
2268 algorithm
2269
2/2
✓ Branch 0 taken 67910 times.
✓ Branch 1 taken 4986 times.
72896 if listEmpty(var.instDims) then
2270 67910 return;
2271 end if;
2272 _ := match var.value
2273 1916 case SOME(DAE.SHARED_LITERAL()) algorithm return; then ();
2274 else ();
2275 end match;
2276 _ := match Types.arrayElementType(var.ty)
2277 case DAE.T_REAL() then ();
2278 case DAE.T_INTEGER() then ();
2279 case DAE.T_BOOL() then ();
2280 34 else algorithm return; then ();
2281 end match;
2282
2/2
✓ Branch 0 taken 3378 times.
✓ Branch 1 taken 2506 times.
5884 for v in outVars loop
2283 _ := match v
2284 case SimCodeFunction.VARIABLE() guard ComponentReferenceBasics.crefEqual(v.name, var.name)
2285 530 algorithm return; then ();
2286 else ();
2287 end match;
2288 end for;
2289 n := 1;
2290
2/2
✓ Branch 0 taken 2566 times.
✓ Branch 1 taken 2506 times.
5072 for d in var.instDims loop
2291 n := match d
2292 2382 case DAE.DIM_INTEGER() guard d.integer > 0 then n * d.integer;
2293 else 0;
2294 end match;
2295 end for;
2296
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2506 times.
2506 if n > 256 then
2297 n := 0;
2298 end if;
2299 then ();
2300 else ();
2301 end match;
2302 end stackArrayLength;
2303
2304 public function isJacobianColumnCref
2305 "Whether cr is x.$pDER<M>.dummyVar<M>, an element of a Jacobian column. The
2306 Jacobian only has the elements that depend on the seeds; the others are zero."
2307 input DAE.ComponentRef cr;
2308 output Boolean b;
2309 algorithm
2310 b := match cr
2311 local
2312 String id, last;
2313 case DAE.CREF_QUAL(ident = id, componentRef = DAE.CREF_IDENT(ident = last))
2314
3/4
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 22826 times.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
22843 then StringUtil.startsWith(id, DAE.partialDerivativeNamePrefix) and StringUtil.startsWith(last, "dummyVar");
2315 28028 case DAE.CREF_QUAL() then isJacobianColumnCref(cr.componentRef);
2316 else false;
2317 end match;
2318 end isJacobianColumnCref;
2319
2320 public function cref2simvar
2321 "Used by templates to find SIMVAR for given cref (to gain representaion index info mainly)."
2322 input DAE.ComponentRef inCref;
2323 input SimCode.SimCode simCode;
2324 output SimCodeVar.SimVar outSimVar;
2325 protected
2326 HashTableCrefSimVar.HashTable crefToSimVarHT;
2327 DAE.ComponentRef cref, badcref;
2328 algorithm
2329 try
2330 2149196 SimCode.SIMCODE(crefToSimVarHT = crefToSimVarHT) := simCode;
2331
2/2
✓ Branch 0 taken 7145 times.
✓ Branch 1 taken 2142051 times.
2149196 cref := if simCode.scalarized then inCref else ComponentReference.crefStripSubs(inCref);
2332 2149196 outSimVar := simVarFromHT(cref, crefToSimVarHT);
2333 // print("cref2simvar found via HT for cref: " + ComponentReferenceBasics.printComponentRefStr(outSimVar.name) + "\n");
2334 else
2335 // print("cref2simvar: " + ComponentReferenceBasics.printComponentRefStr(inCref) + " not found!\n");
2336 ✗ badcref := ComponentReferenceBasics.makeCrefIdent("ERROR_cref2simvar_failed " + ComponentReferenceBasics.printComponentRefStr(inCref), DAE.T_REAL_DEFAULT, {});
2337 ✗ outSimVar := SimCodeVar.SIMVAR(badcref, BackendDAE.VARIABLE(), "", "", "", -2, NONE(), NONE(), NONE(), NONE(), false, DAE.T_REAL_DEFAULT, false, NONE(), SimCodeVar.NOALIAS(), DAE.emptyElementSource, SOME(SimCodeVar.LOCAL()), NONE(), NONE(), {}, false, true, NONE(), false, NONE(), false, NONE(), NONE(), NONE(), SOME(badcref), false, false);
2338 end try;
2339 end cref2simvar;
2340
2341 public function simVarExactFromHT
2342 "Used by templates to find the SIMVAR that is stored for exactly this cref (no array offset lookup)."
2343 input DAE.ComponentRef inCref;
2344 input HashTableCrefSimVar.HashTable crefToSimVarHT;
2345 output Option<SimCodeVar.SimVar> outSimVar;
2346 algorithm
2347
2/2
✓ Branch 1 taken 4338 times.
✓ Branch 2 taken 1749 times.
6087 outSimVar := if BaseHashTable.hasKey(inCref, crefToSimVarHT) then SOME(BaseHashTable.get(inCref, crefToSimVarHT)) else NONE();
2348 end simVarExactFromHT;
2349
2350 public function simVarFromHT
2351 "Used by templates to find SIMVAR for given cref (to gain representaion index info mainly)."
2352 input DAE.ComponentRef inCref;
2353 input HashTableCrefSimVar.HashTable crefToSimVarHT;
2354 output SimCodeVar.SimVar outSimVar;
2355 protected
2356 DAE.ComponentRef cref, badcref;
2357 SimCodeVar.SimVar sv;
2358 list<DAE.Subscript> subs;
2359 algorithm
2360 try
2361
2/2
✓ Branch 1 taken 2180197 times.
✓ Branch 2 taken 119470 times.
2299667 if BaseHashTable.hasKey(inCref, crefToSimVarHT) then
2362 2180197 sv := BaseHashTable.get(inCref, crefToSimVarHT);
2363 else
2364 // lookup array variable and add offset for array element
2365
2/2
✓ Branch 1 taken 101580 times.
✓ Branch 2 taken 17890 times.
119470 if Flags.isSet(Flags.NF_SCALARIZE) then
2366 101580 sv := BaseHashTable.get(ComponentReferenceBasics.crefStripLastSubs(inCref), crefToSimVarHT);
2367 183 subs := ComponentReference.crefLastSubs(inCref);
2368 183 sv.name := ComponentReference.crefSetLastSubs(sv.name, subs);
2369 else
2370 17890 sv := BaseHashTable.get(ComponentReference.crefStripSubs(inCref), crefToSimVarHT);
2371 431 subs := ComponentReferenceBasics.crefSubs(inCref);
2372 431 sv.name := ComponentReference.crefApplySubs(ComponentReference.crefStripSubs(sv.name), subs);
2373 end if;
2374
2375 sv.variable_index := match sv.variable_index
2376 local Integer index;
2377 case SOME(index)
2378 614 then SOME(index + SimCodeUtilShared.getScalarElementIndex(subs, List.map(sv.numArrayElement, stringInt)) - 1);
2379 else sv.variable_index;
2380 end match;
2381 // fix fmi_index when using nfScalarize
2382 sv.fmi_index := match sv.fmi_index
2383 local Integer fmiIndex;
2384 case SOME(fmiIndex)
2385 381 then SOME(fmiIndex + SimCodeUtilShared.getScalarElementIndex(subs, List.map(sv.numArrayElement, stringInt)) - 1);
2386 else sv.fmi_index;
2387 end match;
2388 end if;
2389 sv := match sv.aliasvar
2390 case SimCodeVar.NOALIAS() then sv;
2391 279 case SimCodeVar.ALIAS(varName=cref) then simVarFromHT(cref, crefToSimVarHT); /* Possibly not needed; can't really hurt that much though */
2392 case SimCodeVar.NEGATEDALIAS() then sv;
2393 end match;
2394 else
2395 //print("cref2simvar: " + ComponentReferenceBasics.printComponentRefStr(inCref) + " not found!\n");
2396 118964 badcref := ComponentReferenceBasics.makeCrefIdent("ERROR_simVarFromHT_failed " + ComponentReferenceBasics.printComponentRefStr(inCref), DAE.T_REAL_DEFAULT, {});
2397 118964 sv := SimCodeVar.SIMVAR(badcref, BackendDAE.VARIABLE(), "", "", "", -2, NONE(), NONE(), NONE(), NONE(), false, DAE.T_REAL_DEFAULT, false, NONE(), SimCodeVar.NOALIAS(), DAE.emptyElementSource, SOME(SimCodeVar.LOCAL()), NONE(), NONE(), {}, false, true, NONE(), false, NONE(), false, NONE(), NONE(), NONE(), SOME(badcref), false, false);
2398 end try;
2399 outSimVar := sv;
2400 end simVarFromHT;
2401
2402 public function createJacContext
2403 input String name;
2404 input Option<HashTableCrefSimVar.HashTable> jacHT;
2405 output SimCodeFunction.Context outContext;
2406 algorithm
2407 34035 outContext := SimCodeFunction.JACOBIAN_CONTEXT(name, jacHT);
2408 end createJacContext;
2409
2410 public function codegenExpSanityCheck "Handle some things that Susan cannot handle:
2411 * Expand simulation context arrays that contain variables stored in different locations...
2412 * We could move collapsing arrays here since it should be safer to do so when we can lookup which index a variable corresponds to...
2413 * Drop the boxing around calls through a function value (unboxFunctionReferenceCall).
2414 "
2415 input output DAE.Exp e;
2416 input SimCodeFunction.Context context;
2417 algorithm
2418 2676177 e := unboxFunctionReferenceCall(e);
2419
2/2
✓ Branch 1 taken 955867 times.
✓ Branch 2 taken 1720310 times.
2676177 if SimCodeFunctionUtil.inFunctionContext(context) then
2420 955867 return;
2421 end if;
2422
2423 e := match e
2424 local
2425 list<SimCodeVar.SimVar> vars;
2426 SimCode.SimCode simCode;
2427 SimCodeVar.SimVar prev;
2428 list<DAE.ComponentRef> crf_lst;
2429 case DAE.CREF(ty=DAE.T_ARRAY())
2430 algorithm
2431 1855 simCode := getSimCode();
2432 1855 crf_lst := ComponentReference.expandCref(e.componentRef, true);
2433
4/4
✓ Branch 0 taken 11783 times.
✓ Branch 1 taken 1855 times.
✓ Branch 2 taken 11783 times.
✓ Branch 3 taken 1855 times.
13638 vars := list(cref2simvar(cr, simCode) for cr in crf_lst);
2434
2/2
✓ Branch 0 taken 1845 times.
✓ Branch 1 taken 10 times.
1855 if not listEmpty(vars) then
2435 1845 prev::vars := vars;
2436
2/2
✓ Branch 0 taken 9326 times.
✓ Branch 1 taken 1768 times.
11094 for v in vars loop
2437 // The array needs to be expanded because it's not stored in contiguous memory.
2438 // Without scalarization the elements of an array variable are one SimVar.
2439
5/6
✓ Branch 0 taken 362 times.
✓ Branch 1 taken 8964 times.
✓ Branch 2 taken 285 times.
✓ Branch 3 taken 77 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 285 times.
9326 if not (v.index == prev.index + 1 or (not simCode.scalarized and isSameArrayVar(v, prev))) then
2440 77 e := Expression.expandCrefs(e, false /*do not expand records*/);
2441 77 break;
2442 end if;
2443 prev := v;
2444 end for;
2445 end if;
2446 then e;
2447 else e;
2448 end match;
2449 end codegenExpSanityCheck;
2450
2451 protected function isSameArrayVar
2452 "Whether two elements of a non-scalarized array belong to the same SimVar.
2453 The index alone is not enough, it is only unique within a kind of variable."
2454 input SimCodeVar.SimVar v1;
2455 input SimCodeVar.SimVar v2;
2456 output Boolean b = v1.index == v2.index and valueEq(v1.varKind, v2.varKind)
2457 and ComponentReferenceBasics.crefEqualNoStringCompare(ComponentReference.crefStripSubs(v1.name), ComponentReference.crefStripSubs(v2.name));
2458 end isSameArrayVar;
2459
2460 public function unboxFunctionReferenceCall
2461 "Drops the boxing around a call through a function value: C calls it with the
2462 unboxed signature of the function it refers to. MetaModelica keeps it, since
2463 a polymorphic function needs it and a closure there can outlive its frame."
2464 input output DAE.Exp exp;
2465 protected
2466 DAE.Exp e;
2467 DAE.CallAttributes attr;
2468 algorithm
2469
2/2
✓ Branch 1 taken 49597 times.
✓ Branch 2 taken 2629318 times.
2678915 if Config.acceptMetaModelicaGrammar() then
2470 49597 return;
2471 end if;
2472
2473 exp := match exp
2474 case DAE.UNBOX(exp = e as DAE.CALL(attr = DAE.CALL_ATTR(isFunctionPointerCall = true)))
2475 64 then unboxFunctionReferenceCall(e);
2476 case DAE.UNBOX(exp = e as DAE.TSUB(exp = DAE.CALL(attr = DAE.CALL_ATTR(isFunctionPointerCall = true))))
2477 ✗ then unboxFunctionReferenceCall(e);
2478 case DAE.TSUB(exp = e as DAE.CALL(attr = DAE.CALL_ATTR(isFunctionPointerCall = true)))
2479 algorithm
2480 ✗ exp.exp := unboxFunctionReferenceCall(e);
2481 ✗ exp.ty := Types.unboxedType(exp.ty);
2482 then exp;
2483 case DAE.CALL(attr = attr as DAE.CALL_ATTR(isFunctionPointerCall = true))
2484 algorithm
2485
4/4
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 64 times.
✓ Branch 2 taken 64 times.
✓ Branch 3 taken 64 times.
192 exp.expLst := list(unboxArgument(a) for a in exp.expLst);
2486 64 attr.ty := unboxResultType(attr.ty);
2487 64 exp.attr := attr;
2488 then exp;
2489 case DAE.PARTEVALFUNCTION()
2490 algorithm
2491
4/4
✓ Branch 0 taken 403 times.
✓ Branch 1 taken 151 times.
✓ Branch 2 taken 403 times.
✓ Branch 3 taken 151 times.
705 exp.expList := list(unboxArgument(a) for a in exp.expList);
2492 151 exp.ty := unboxFunctionReferenceType(exp.ty);
2493 151 exp.origType := unboxFunctionReferenceType(exp.origType);
2494 then exp;
2495 else exp;
2496 end match;
2497 end unboxFunctionReferenceCall;
2498
2499 protected function unboxArgument
2500 "A boxed record literal is a METARECORDCALL with boxed fields."
2501 input output DAE.Exp exp;
2502 protected
2503 DAE.Exp e;
2504 list<DAE.Exp> args;
2505 algorithm
2506 exp := match exp
2507 507 case DAE.BOX() then unboxFunctionReferenceCall(exp.exp);
2508 case DAE.METARECORDCALL(index = -1)
2509 algorithm
2510
4/4
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 5 times.
55 args := list(unboxArgument(a) for a in exp.args);
2511
7/8
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 50 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 50 times.
✓ Branch 6 taken 5 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
55 then
2512 DAE.RECORD(exp.path, args, exp.fieldNames,
2513 DAE.T_COMPLEX(ClassInf.RECORD(exp.path),
2514 list(DAE.TYPES_VAR(n, DAE.dummyAttrVar, Expression.typeof(a), DAE.UNBOUND(), false, NONE())
2515 threaded for a in args, n in exp.fieldNames),
2516 NONE(), false));
2517 30 case DAE.SHARED_LITERAL(exp = e as DAE.BOX()) then unboxArgument(e);
2518 ✗ case DAE.SHARED_LITERAL(exp = e as DAE.METARECORDCALL(index = -1)) then unboxArgument(e);
2519 5 else unboxFunctionReferenceCall(exp);
2520 end match;
2521 end unboxArgument;
2522
2523 protected function unboxResultType
2524 input output DAE.Type ty;
2525 algorithm
2526 ty := match ty
2527 case DAE.T_TUPLE()
2528 algorithm
2529 ✗ ty.types := list(Types.unboxedType(t) for t in ty.types);
2530 then ty;
2531 366 else Types.unboxedType(ty);
2532 end match;
2533 end unboxResultType;
2534
2535 protected function unboxFunctionReferenceType
2536 input output DAE.Type ty;
2537 protected
2538 DAE.Type fty;
2539 algorithm
2540 ty := match ty
2541 case DAE.T_FUNCTION_REFERENCE_VAR(functionType = fty as DAE.T_FUNCTION())
2542 algorithm
2543
4/4
✓ Branch 0 taken 705 times.
✓ Branch 1 taken 302 times.
✓ Branch 2 taken 705 times.
✓ Branch 3 taken 302 times.
1309 fty.funcArg := list(unboxFuncArg(a) for a in fty.funcArg);
2544 302 fty.funcResultType := unboxResultType(fty.funcResultType);
2545 302 ty.functionType := fty;
2546 then ty;
2547 else ty;
2548 end match;
2549 end unboxFunctionReferenceType;
2550
2551 protected function unboxFuncArg
2552 input output DAE.FuncArg arg;
2553 algorithm
2554 arg := match arg
2555 case DAE.FUNCARG()
2556 algorithm
2557 705 arg.ty := Types.unboxedType(arg.ty);
2558 then arg;
2559 end match;
2560 end unboxFuncArg;
2561
2562 public function absoluteClockIdxForBaseClock
2563 input Integer baseClockIdx; // one-based
2564 input list<SimCode.ClockedPartition> allBaseClockPartitions;
2565 output Integer absBaseClockIdx;
2566 protected
2567 Integer i = 1;
2568 algorithm
2569 absBaseClockIdx := 1;
2570
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 while i < baseClockIdx loop
2571 4 absBaseClockIdx := absBaseClockIdx + listLength(getSubPartition(listGet(allBaseClockPartitions,i)));
2572 4 i := i+1;
2573 end while;
2574 end absoluteClockIdxForBaseClock;
2575
2576 public function getClockedPartitions
2577 input SimCode.SimCode simcode;
2578 output list<SimCode.ClockedPartition> clockedPartitions;
2579 algorithm
2580 6 clockedPartitions := simcode.clockedPartitions;
2581 end getClockedPartitions;
2582
2583 public function isScalarLiteralAssignment
2584 input SimCode.SimEqSystem eq;
2585 output Boolean b;
2586 algorithm
2587 b := match eq
2588 201638 case SimCode.SES_SIMPLE_ASSIGN() then Expression.isSimpleLiteralValue(eq.exp);
2589 else false;
2590 end match;
2591 end isScalarLiteralAssignment;
2592
2593 public function selectScalarLiteralAssignments
2594 input output list<SimCode.SimEqSystem> eqs;
2595 algorithm
2596
6/6
✓ Branch 1 taken 81083 times.
✓ Branch 2 taken 80547 times.
✓ Branch 3 taken 161630 times.
✓ Branch 4 taken 1204 times.
✓ Branch 5 taken 80547 times.
✓ Branch 6 taken 1204 times.
162834 eqs := list(e for e guard isScalarLiteralAssignment(e) in eqs);
2597 end selectScalarLiteralAssignments;
2598
2599 public function filterScalarLiteralAssignments
2600 input output list<SimCode.SimEqSystem> eqs;
2601 algorithm
2602
6/6
✓ Branch 1 taken 80547 times.
✓ Branch 2 taken 81083 times.
✓ Branch 3 taken 161630 times.
✓ Branch 4 taken 1204 times.
✓ Branch 5 taken 81083 times.
✓ Branch 6 taken 1204 times.
162834 eqs := list(e for e guard not isScalarLiteralAssignment(e) in eqs);
2603 end filterScalarLiteralAssignments;
2604
2605 public function sortSimpleAssignmentBasedOnLhs
2606 input output list<SimCode.SimEqSystem> eqs;
2607 protected
2608 SimCode.SimCode simCode = getSimCode();
2609 algorithm
2610
8/8
✓ Branch 0 taken 80547 times.
✓ Branch 1 taken 1204 times.
✓ Branch 2 taken 80547 times.
✓ Branch 3 taken 1204 times.
✓ Branch 7 taken 80547 times.
✓ Branch 8 taken 1204 times.
✓ Branch 9 taken 80547 times.
✓ Branch 10 taken 1204 times.
162298 eqs := list(Util.tuple21(e) for e in List.sort(list((eq, lhsSortKey(eq, simCode)) for eq in eqs), keyedLhsGreaterThan));
2611 end sortSimpleAssignmentBasedOnLhs;
2612
2613 protected function lhsSortKey
2614 "Type, kind and index of a simple assignment's variable; none for other equations."
2615 input SimCode.SimEqSystem eq;
2616 input SimCode.SimCode simCode;
2617 output Option<tuple<Integer, Integer, Integer>> key;
2618 algorithm
2619 key := match eq
2620 local
2621 SimCodeVar.SimVar v;
2622 case SimCode.SES_SIMPLE_ASSIGN()
2623 algorithm
2624 80547 v := cref2simvar(eq.cref, simCode);
2625 80547 then SOME((valueConstructor(v.type_), valueConstructor(v.varKind), v.index));
2626 else NONE();
2627 end match;
2628 end lhsSortKey;
2629
2630 protected function keyedLhsGreaterThan
2631 input tuple<SimCode.SimEqSystem, Option<tuple<Integer, Integer, Integer>>> e1, e2;
2632 output Boolean b;
2633 algorithm
2634 b := match (e1, e2)
2635 local
2636 Integer t1, k1, i1, t2, k2, i2;
2637 case ((_, SOME((t1, k1, i1))), (_, SOME((t2, k2, i2))))
2638
4/4
✓ Branch 0 taken 625578 times.
✓ Branch 1 taken 218787 times.
✓ Branch 2 taken 617557 times.
✓ Branch 3 taken 8021 times.
844365 then if t1 == t2 then (if k1 == k2 then i1 > i2 else k1 > k2) else t1 > t2;
2639 else false;
2640 end match;
2641 end keyedLhsGreaterThan;
2642
2643 public function getNumContinuousEquations
2644 input list<SimCode.SimEqSystem> eqns;
2645 input Integer numStates;
2646 output Integer n;
2647 protected
2648 Integer numEqns =0;
2649 algorithm
2650
2/2
✓ Branch 0 taken 2187 times.
✓ Branch 1 taken 49 times.
2236 for eqn in eqns loop
2651 2187 numEqns := numEqns + getNumContinuousEquationsSingleEq(eqn);
2652 end for;
2653 49 n := numEqns+numStates;
2654 end getNumContinuousEquations;
2655
2656 protected function getNumContinuousEquationsSingleEq
2657 input SimCode.SimEqSystem eqn;
2658 output Integer n;
2659 algorithm
2660 n := match eqn
2661 local
2662 SimCode.LinearSystem ls;
2663 SimCode.NonlinearSystem nls;
2664 ✗ case SimCode.SES_MIXED() then getNumContinuousEquationsSingleEq(eqn.cont);
2665 34 case SimCode.SES_LINEAR(lSystem = ls as SimCode.LINEARSYSTEM(__)) then listLength(ls.vars);
2666 1144 case SimCode.SES_NONLINEAR(nlSystem = nls as SimCode.NONLINEARSYSTEM(__)) then listLength(nls.crefs);
2667 else 1;
2668 end match;
2669 end getNumContinuousEquationsSingleEq;
2670
2671 public function lookupVR
2672 input DAE.ComponentRef cr;
2673 input SimCode.SimCode simCode;
2674 output Integer vr;
2675 algorithm
2676 9359 vr := AvlTreeCRToInt.get(simCode.valueReferences, cr);
2677 end lookupVR;
2678
2679 public function isFMUSimCode
2680 "True when this SimCode was built for an FMU export, so `valueReferences` --
2681 what lookupVR and the FMI alias tables index -- is filled."
2682 input SimCode.SimCode simCode;
2683 output Boolean isFMU;
2684 algorithm
2685 isFMU := match simCode.valueReferences
2686 case AvlTreeCRToInt.EMPTY() then false;
2687 else true;
2688 end match;
2689 end isFMUSimCode;
2690
2691 public function lookupVRForRealOutputDerivative
2692 "function which maps output Real var ValueReference to an internal real variable ValueReference of
2693 pattern $X_der where x = varname, this function will be used by fmi2GetRealOutputDerivatives"
2694 input DAE.ComponentRef cr;
2695 input SimCode.SimCode simCode;
2696 input String fmuType;
2697 output Integer vr;
2698 protected
2699 DAE.ComponentRef outputRealDerivativeCref;
2700 algorithm
2701
4/4
✓ Branch 0 taken 81 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 79 times.
87 if (fmuType == "cs") then
2702 // map the cref to the internal real var (e.g) output Real y => $y_der
2703 2 outputRealDerivativeCref := ComponentReference.appendStringLastIdent("_der", cr); // append _der
2704 2 outputRealDerivativeCref := ComponentReference.prependStringCref("$", outputRealDerivativeCref); // prepend $
2705 2 vr := AvlTreeCRToInt.get(simCode.valueReferences, outputRealDerivativeCref);
2706 else
2707 vr := -1;
2708 end if;
2709 end lookupVRForRealOutputDerivative;
2710
2711 public function fmi3ArrayView
2712 "The SimCode the FMI 3.0 modelDescription.xml is rendered from: one SimVar
2713 and one ModelStructure entry per array of modelStructure.fmiArrays."
2714 input SimCode.SimCode simCode;
2715 output SimCode.SimCode view = simCode;
2716 protected
2717 SimCode.FmiModelStructure ms;
2718 SimCode.ModelInfo mi;
2719 SimCodeVar.SimVars vars;
2720 SimCode.FmiInitialUnknowns iu;
2721 UnorderedMap<DAE.ComponentRef, Integer> firsts;
2722 UnorderedMap<Integer, Integer> rep;
2723 algorithm
2724
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
16 if isNone(simCode.modelStructure) then
2725 ✗ return;
2726 end if;
2727 16 SOME(ms) := simCode.modelStructure;
2728
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
16 if listEmpty(ms.fmiArrays) then
2729 14 return;
2730 end if;
2731 2 firsts := UnorderedMap.new<Integer>(ComponentReferenceBasics.hashComponentRef, ComponentReferenceBasics.crefEqual);
2732 2 rep := UnorderedMap.new<Integer>(Util.id, intEq);
2733
2/2
✓ Branch 0 taken 210 times.
✓ Branch 1 taken 2 times.
212 for a in ms.fmiArrays loop
2734 210 UnorderedMap.add(a.first, a.numElements, firsts);
2735
1/2
✓ Branch 0 taken 210 times.
✗ Branch 1 not taken.
774 for k in 1:a.numElements - 1 loop
2736 564 UnorderedMap.add(a.fmiIndex + k, a.fmiIndex, rep);
2737 end for;
2738 end for;
2739 2 mi := simCode.modelInfo;
2740 2 vars := mi.vars;
2741 2 vars.stateVars := fmi3CollapseArrays(vars.stateVars, firsts);
2742 vars.derivativeVars := fmi3CollapseArrays(vars.derivativeVars, firsts);
2743 vars.algVars := fmi3CollapseArrays(vars.algVars, firsts);
2744 vars.discreteAlgVars := fmi3CollapseArrays(vars.discreteAlgVars, firsts);
2745 vars.paramVars := fmi3CollapseArrays(vars.paramVars, firsts);
2746 vars.intAlgVars := fmi3CollapseArrays(vars.intAlgVars, firsts);
2747 vars.intParamVars := fmi3CollapseArrays(vars.intParamVars, firsts);
2748 vars.boolAlgVars := fmi3CollapseArrays(vars.boolAlgVars, firsts);
2749 vars.boolParamVars := fmi3CollapseArrays(vars.boolParamVars, firsts);
2750 vars.stringAlgVars := fmi3CollapseArrays(vars.stringAlgVars, firsts);
2751 vars.stringParamVars := fmi3CollapseArrays(vars.stringParamVars, firsts);
2752 2 mi.vars := vars;
2753 2 view.modelInfo := mi;
2754 2 ms.fmiOutputs := SimCode.FMIOUTPUTS(fmi3CollapseUnknowns(ms.fmiOutputs.fmiUnknownsList, rep));
2755 ms.fmiDerivatives := SimCode.FMIDERIVATIVES(fmi3CollapseUnknowns(ms.fmiDerivatives.fmiUnknownsList, rep));
2756 ms.fmiDiscreteStates := SimCode.FMIDISCRETESTATES(fmi3CollapseUnknowns(ms.fmiDiscreteStates.fmiUnknownsList, rep));
2757 2 iu := ms.fmiInitialUnknowns;
2758 2 iu.fmiUnknownsList := fmi3CollapseUnknowns(iu.fmiUnknownsList, rep);
2759 2 ms.fmiInitialUnknowns := iu;
2760 2 view.modelStructure := SOME(ms);
2761 end fmi3ArrayView;
2762
2763 protected function fmi3CollapseArrays
2764 input list<SimCodeVar.SimVar> vars;
2765 input UnorderedMap<DAE.ComponentRef, Integer> firsts "first element -> number of elements";
2766 output list<SimCodeVar.SimVar> outVars = {};
2767 protected
2768 list<SimCodeVar.SimVar> rest = vars, elements;
2769 SimCodeVar.SimVar v;
2770 Integer n;
2771 algorithm
2772
2/2
✓ Branch 0 taken 1755 times.
✓ Branch 1 taken 22 times.
1777 while not listEmpty(rest) loop
2773 1755 v :: rest := rest;
2774 1755 n := UnorderedMap.getOrDefault(v.name, firsts, 0);
2775
2/2
✓ Branch 0 taken 210 times.
✓ Branch 1 taken 1545 times.
1755 if n > 1 then
2776 210 (elements, rest) := List.split(rest, n - 1);
2777 210 v := fmi3ArrayVar(v, elements);
2778 end if;
2779 outVars := v :: outVars;
2780 end while;
2781 22 outVars := listReverse(outVars);
2782 end fmi3CollapseArrays;
2783
2784 protected function fmi3ArrayVar
2785 "The array variable of first and the elements after it."
2786 input SimCodeVar.SimVar first;
2787 input list<SimCodeVar.SimVar> others;
2788 output SimCodeVar.SimVar var = first;
2789 algorithm
2790
4/4
✓ Branch 0 taken 234 times.
✓ Branch 1 taken 210 times.
✓ Branch 2 taken 234 times.
✓ Branch 3 taken 210 times.
654 var.type_ := DAE.T_ARRAY(first.type_, list(DAE.DIM_INTEGER(stringInt(d)) for d in first.numArrayElement));
2791 var.exportVar := SOME(ComponentReferenceBasics.crefStripLastSubs(Util.getOption(first.exportVar)));
2792 var.initialValue := match first.initialValue
2793
4/4
✓ Branch 0 taken 753 times.
✓ Branch 1 taken 203 times.
✓ Branch 2 taken 753 times.
✓ Branch 3 taken 203 times.
956 case SOME(_) then SOME(DAE.ARRAY(var.type_, true, list(Util.getOption(v.initialValue) for v in first :: others)));
2794 else NONE();
2795 end match;
2796 end fmi3ArrayVar;
2797
2798 protected function fmi3CollapseUnknowns
2799 input list<SimCode.FmiUnknown> unknowns;
2800 input UnorderedMap<Integer, Integer> rep "element FMI index -> the array's";
2801 output list<SimCode.FmiUnknown> outUnknowns = {};
2802 protected
2803 UnorderedMap<Integer, Integer> slot = UnorderedMap.new<Integer>(Util.id, intEq) "representative -> position in deps";
2804 array<list<Integer>> deps = arrayCreate(listLength(unknowns), {});
2805 list<Integer> order = {}, ds;
2806 Integer r, i, n = 0;
2807 algorithm
2808
2/2
✓ Branch 0 taken 821 times.
✓ Branch 1 taken 8 times.
829 for u in unknowns loop
2809 821 r := UnorderedMap.getOrDefault(u.index, rep, u.index);
2810
4/4
✓ Branch 0 taken 320 times.
✓ Branch 1 taken 821 times.
✓ Branch 2 taken 320 times.
✓ Branch 3 taken 821 times.
1141 ds := list(UnorderedMap.getOrDefault(d, rep, d) for d in u.dependencies);
2811 821 i := UnorderedMap.getOrDefault(r, slot, 0);
2812
2/2
✓ Branch 0 taken 401 times.
✓ Branch 1 taken 420 times.
821 if i == 0 then
2813 401 n := n + 1;
2814 i := n;
2815 401 UnorderedMap.add(r, i, slot);
2816 order := r :: order;
2817 end if;
2818 821 arrayUpdate(deps, i, listAppend(ds, arrayGet(deps, i)));
2819 end for;
2820
2/2
✓ Branch 0 taken 401 times.
✓ Branch 1 taken 8 times.
409 for r in order loop
2821 401 ds := List.sortedUnique(List.sort(arrayGet(deps, UnorderedMap.getOrFail(r, slot)), intGt), intEq);
2822 401 outUnknowns := SimCode.FMIUNKNOWN(r, ds, List.fill("dependent", listLength(ds))) :: outUnknowns;
2823 end for;
2824 end fmi3CollapseUnknowns;
2825
2826 public function fmi3ArrayDefines
2827 "The FMI 3.0 array variables as (value reference, number of elements) tables
2828 for fmu3_model_interface.c, sorted by value reference."
2829 input SimCode.SimCode simCode;
2830 output String defines;
2831 protected
2832 list<tuple<Integer, Integer>> arrays = {};
2833 SimCode.HashTableCrefToSimVar ht;
2834 SimCodeVar.SimVar v;
2835 algorithm
2836 _ := match simCode.modelStructure
2837 local SimCode.FmiModelStructure ms;
2838 case SOME(ms) guard not listEmpty(ms.fmiArrays)
2839 algorithm
2840 2 ht := createCrefToSimVarHT(simCode.modelInfo);
2841
2/2
✓ Branch 0 taken 210 times.
✓ Branch 1 taken 2 times.
212 for a in ms.fmiArrays loop
2842 210 v := BaseHashTable.get(a.first, ht);
2843 210 arrays := (stringInt(getFMI3ValueReference(v, simCode)), a.numElements) :: arrays;
2844 end for;
2845 2 arrays := List.sort(arrays, Util.compareTupleIntGt);
2846 then ();
2847 else ();
2848 end match;
2849
8/8
✓ Branch 5 taken 210 times.
✓ Branch 6 taken 15 times.
✓ Branch 7 taken 210 times.
✓ Branch 8 taken 15 times.
✓ Branch 15 taken 210 times.
✓ Branch 16 taken 15 times.
✓ Branch 17 taken 210 times.
✓ Branch 18 taken 15 times.
435 defines := "#define FMI3_NUMBER_OF_ARRAYS " + intString(listLength(arrays)) + "\n"
2850 + "#define FMI3_ARRAY_VRS { " + stringDelimitList(list(intString(Util.tuple21(a)) for a in arrays), ", ") + " }\n"
2851 + "#define FMI3_ARRAY_LENGTHS { " + stringDelimitList(list(intString(Util.tuple22(a)) for a in arrays), ", ") + " }";
2852 end fmi3ArrayDefines;
2853
2854 public function isFMI3NestableAlias
2855 "True if a SimVar can be represented as an FMI 3.0 <Alias> child element of its
2856 canonical variable (sharing the canonical valueReference) instead of a separate
2857 ModelVariables entry. Only positive (non-negated) scalar aliases with no
2858 causality of their own (local) qualify: an <Alias> element carries no factor and
2859 no causality, so negated aliases and input/output/parameter aliases must stay
2860 as full variables."
2861 input SimCodeVar.SimVar simVar;
2862 output Boolean nestable;
2863 algorithm
2864 nestable := match simVar
2865 case SimCodeVar.SIMVAR(aliasvar = SimCodeVar.ALIAS())
2866 guard isSome(simVar.exportVar)
2867 and not Types.isArray(simVar.type_)
2868 and (match simVar.causality
2869 case NONE() then true;
2870 case SOME(SimCodeVar.LOCAL()) then true;
2871 case SOME(SimCodeVar.NONECAUS()) then true;
2872 else false;
2873 end match)
2874 then true;
2875 else false;
2876 end match;
2877 end isFMI3NestableAlias;
2878
2879 protected function fmi3AliasTargetValueReference
2880 "The value reference the nestable alias `v` shares with its target, i.e. the
2881 value reference of the canonical variable it is an <Alias> of. `None` when the
2882 target is not a variable this FMU exports."
2883 input SimCodeVar.SimVar v;
2884 input SimCode.SimCode simCode;
2885 output Option<Integer> vr;
2886 algorithm
2887 vr := match v.aliasvar
2888 local
2889 DAE.ComponentRef cr;
2890 Integer local_;
2891 case SimCodeVar.ALIAS(varName = cr)
2892 then match AvlTreeCRToInt.getOpt(simCode.valueReferences, cr)
2893 1616 case SOME(local_) then SOME(getFMI3TypeOffset(v.type_, simCode.modelInfo) + local_);
2894 else NONE();
2895 end match;
2896 else NONE();
2897 end match;
2898 end fmi3AliasTargetValueReference;
2899
2900 public function cacheFMI3VariableAliases
2901 "Build the value reference -> <Alias> members table getFMI3VariableAliases reads,
2902 and keep it for as long as one modelDescription.xml is being written (see
2903 clearFMI3VariableAliases).
2904
2905 Without it every variable emitted searches every alias the model has for the
2906 ones nested under it, which is quadratic and is what rendering an FMI 3.0
2907 modelDescription.xml costs: 14 of FullRobot's 24 export seconds."
2908 input SimCode.SimCode simCode;
2909 // Susan calls this for its effect; the empty string is what it interpolates.
2910 output String dummy = "";
2911 protected
2912 SimCodeVar.SimVars vars = simCode.modelInfo.vars;
2913 list<list<SimCodeVar.SimVar>> aliasLists =
2914 {vars.aliasVars, vars.intAliasVars, vars.boolAliasVars, vars.stringAliasVars};
2915 array<list<SimCodeVar.SimVar>> table;
2916 Integer n = 0, vr;
2917 algorithm
2918 // Two passes: the table is indexed by value reference, whose range is only
2919 // known once every alias has been resolved.
2920
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 16 times.
80 for lst in aliasLists loop
2921
2/2
✓ Branch 0 taken 1323 times.
✓ Branch 1 taken 64 times.
1387 for v in lst loop
2922
2/2
✓ Branch 1 taken 808 times.
✓ Branch 2 taken 515 times.
1323 if isFMI3NestableAlias(v) then
2923 808 n := match fmi3AliasTargetValueReference(v, simCode) case SOME(vr) then intMax(n, vr + 1); else n; end match;
2924 end if;
2925 end for;
2926 end for;
2927 16 table := arrayCreate(n, {});
2928
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 16 times.
80 for lst in aliasLists loop
2929
2/2
✓ Branch 0 taken 1323 times.
✓ Branch 1 taken 64 times.
1387 for v in lst loop
2930
2/2
✓ Branch 1 taken 808 times.
✓ Branch 2 taken 515 times.
1323 if isFMI3NestableAlias(v) then
2931 _ := match fmi3AliasTargetValueReference(v, simCode)
2932 case SOME(vr)
2933 1616 algorithm arrayUpdate(table, vr + 1, v :: arrayGet(table, vr + 1)); then ();
2934 else ();
2935 end match;
2936 end if;
2937 end for;
2938 end for;
2939
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 12 times.
3331 for i in 1:n loop
2940 3315 arrayUpdate(table, i, listReverse(arrayGet(table, i)));
2941 end for;
2942 16 setGlobalRoot(Global.fmi3VariableAliasCache, SOME(table));
2943 end cacheFMI3VariableAliases;
2944
2945 public function clearFMI3VariableAliases
2946 "Drop what cacheFMI3VariableAliases built, so the next model builds its own."
2947 output String dummy = "";
2948 algorithm
2949 16 setGlobalRoot(Global.fmi3VariableAliasCache, NONE());
2950 end clearFMI3VariableAliases;
2951
2952 public function getFMI3VariableAliases
2953 "Return the SimVars that are FMI 3.0 <Alias> members of `canonical`: the nestable
2954 (see isFMI3NestableAlias) positive aliases whose alias target is `canonical`.
2955 FMI 3.0 represents these as <Alias> child elements sharing the canonical
2956 variable's valueReference, rather than as separate variables."
2957 input SimCode.SimCode simCode;
2958 input SimCodeVar.SimVar canonical;
2959 output list<SimCodeVar.SimVar> aliases = {};
2960 protected
2961 SimCodeVar.SimVars vars = simCode.modelInfo.vars;
2962 Option<array<list<SimCodeVar.SimVar>>> cached;
2963 array<list<SimCodeVar.SimVar>> table;
2964 Integer vr;
2965 algorithm
2966 1009 cached := getGlobalRoot(Global.fmi3VariableAliasCache);
2967
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1009 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1009 times.
1009 if isSome(cached) then
2968 1009 SOME(table) := cached;
2969 vr := getFMI3TypeOffset(canonical.type_, simCode.modelInfo)
2970 + (match AvlTreeCRToInt.getOpt(simCode.valueReferences, canonical.name)
2971 local Integer local_;
2972 case SOME(local_) then local_;
2973 else -1;
2974 end match);
2975
3/4
✓ Branch 0 taken 1009 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 703 times.
✓ Branch 3 taken 306 times.
2018 if vr >= 0 and vr < arrayLength(table) then
2976 703 aliases := arrayGet(table, vr + 1);
2977 end if;
2978 1009 return;
2979 end if;
2980 ✗ for lst in {vars.aliasVars, vars.intAliasVars, vars.boolAliasVars, vars.stringAliasVars} loop
2981 ✗ for v in lst loop
2982 ✗ if isFMI3NestableAlias(v) then
2983 _ := match v.aliasvar
2984 local DAE.ComponentRef cr;
2985 case SimCodeVar.ALIAS(varName = cr)
2986 guard ComponentReferenceBasics.crefEqualNoStringCompare(cr, canonical.name)
2987 algorithm aliases := v :: aliases; then ();
2988 else ();
2989 end match;
2990 end if;
2991 end for;
2992 end for;
2993 ✗ aliases := listReverse(aliases);
2994 end getFMI3VariableAliases;
2995
2996 public function getFMI3Terminals
2997 "Collect the FMI 3.0 terminals from the exported SimVars. The flat-model type of
2998 each variable tells us whether it stems from a connector: a variable whose cref
2999 has a connector-typed qualifier (identType = T_COMPLEX / T_SUBTYPE_BASIC with
3000 ClassInf.CONNECTOR) is a member of that connector instance. Members are grouped
3001 by their connector instance (the terminal), preserving the variable order. Used
3002 by CodegenFMU3 to emit terminalsAndIcons.xml."
3003 input SimCode.SimCode simCode;
3004 output list<SimCode.FmiTerminal> terminals = {};
3005 protected
3006 SimCodeVar.SimVars vars;
3007 list<SimCodeVar.SimVar> allVars;
3008 list<tuple<String, String, Boolean, SimCode.FmiTerminalMember>> flat = {};
3009 list<String> names = {};
3010 list<String> memberNames;
3011 Option<tuple<String, String, Boolean, SimCode.FmiTerminalMember>> om;
3012 String tname, tname2, tkind, tkind2;
3013 Boolean texp, texp2;
3014 SimCode.FmiTerminalMember mem;
3015 list<SimCode.FmiTerminalMember> mems;
3016 algorithm
3017 30 vars := simCode.modelInfo.vars;
3018 // Gather the variables that also end up in modelDescription.xml. The alias var
3019 // lists are included on purpose: a connector member can itself be an alias
3020 // (e.g. flange_a.phi == flange_b.phi == the state phi). Such a member is a real
3021 // <Terminal> member and is emitted in modelDescription.xml (CodegenFMU3 writes
3022 // the alias var lists into ModelVariables), so its canonical variable (`phi`) is
3023 // NOT the connector member and dropping the alias would lose the member entirely.
3024 // connectorMemberOf filters to connector members, so non-connector aliases are
3025 // ignored. Real vars come first so the canonical member ordering is preserved.
3026 30 allVars := List.flatten({vars.stateVars, vars.derivativeVars, vars.algVars,
3027 vars.discreteAlgVars, vars.paramVars, vars.intAlgVars, vars.intParamVars,
3028 vars.boolAlgVars, vars.boolParamVars, vars.stringAlgVars, vars.stringParamVars,
3029 vars.aliasVars, vars.intAliasVars, vars.boolAliasVars, vars.stringAliasVars});
3030
2/2
✓ Branch 0 taken 7818 times.
✓ Branch 1 taken 30 times.
7848 for v in allVars loop
3031 7818 om := connectorMemberOf(v);
3032
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7818 times.
✓ Branch 2 taken 2156 times.
✓ Branch 3 taken 5662 times.
7818 if isSome(om) then
3033 2156 flat := Util.getOption(om) :: flat;
3034 end if;
3035 end for;
3036 30 flat := listReverse(flat);
3037 // distinct terminal names in first-seen order
3038
2/2
✓ Branch 0 taken 2156 times.
✓ Branch 1 taken 30 times.
2186 for t in flat loop
3039 2156 (tname, _, _, _) := t;
3040
2/2
✓ Branch 1 taken 252 times.
✓ Branch 2 taken 1904 times.
2156 if not listMember(tname, names) then
3041 names := tname :: names;
3042 end if;
3043 end for;
3044 30 names := listReverse(names);
3045 // one terminal per connector instance, members in first-seen order. memberName
3046 // must be unique per terminal (FMI 3.0), so skip a member whose name was already
3047 // added (e.g. a real var and an alias mapping to the same connector member).
3048
2/2
✓ Branch 0 taken 252 times.
✓ Branch 1 taken 30 times.
282 for nm in names loop
3049 mems := {};
3050 memberNames := {};
3051 texp := false;
3052 tkind := "";
3053
2/2
✓ Branch 0 taken 123116 times.
✓ Branch 1 taken 252 times.
123368 for t in flat loop
3054 123116 (tname2, tkind2, texp2, mem) := t;
3055
6/6
✓ Branch 0 taken 20312 times.
✓ Branch 1 taken 102804 times.
✓ Branch 3 taken 2156 times.
✓ Branch 4 taken 18156 times.
✓ Branch 6 taken 744 times.
✓ Branch 7 taken 1412 times.
123116 if stringEq(tname2, nm) and not listMember(mem.memberName, memberNames) then
3056 mems := mem :: mems;
3057 744 memberNames := mem.memberName :: memberNames;
3058 texp := texp2;
3059 tkind := tkind2;
3060 end if;
3061 end for;
3062
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
504 terminals := SimCode.FMI_TERMINAL(nm, tkind, texp, listReverse(mems)) :: terminals;
3063 end for;
3064 // Append the simple signal ports: top-level scalar input/output variables. A
3065 // signal connector (e.g. Modelica.Blocks.Interfaces.RealInput/RealOutput, the
3066 // short class `connector RealInput = input Real`) collapses to a plain
3067 // input/output Real in the flat model, so it cannot be told apart from a
3068 // structured connector member by the cref type. Instead we take the model's
3069 // input/output interface variables (already partitioned by causality in the
3070 // flat model, no annotation/JSON needed) and make each top-level scalar its own
3071 // single-member terminal; structured connector members are qualified crefs and
3072 // are already grouped above, so they are skipped here.
3073 30 terminals := listAppend(listReverse(terminals), simplePortTerminals(vars, names));
3074 end getFMI3Terminals;
3075
3076 protected function simplePortTerminals
3077 "One single-member terminal per top-level scalar input/output variable (the FMU
3078 signal ports). These come from a signal connector (e.g. RealInput/RealOutput)
3079 that collapsed to a plain input/output Real, so the member is a `signal`
3080 variableKind (a non-flow value intended to be equal across a connection); the
3081 connector type was lost in the flat model, so terminalKind is left empty. Skips
3082 variables already part of a structured connector terminal (`taken`)."
3083 input SimCodeVar.SimVars vars;
3084 input list<String> taken;
3085 output list<SimCode.FmiTerminal> terminals = {};
3086 protected
3087 list<String> seen = taken;
3088 algorithm
3089
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 30 times.
46 for v in listAppend(vars.inputVars, vars.outputVars) loop
3090 terminals := matchcontinue v
3091 local
3092 DAE.ComponentRef cr;
3093 String nm;
3094 // a top-level scalar interface variable: cref is a bare identifier
3095 case _ guard isSome(v.exportVar)
3096 algorithm
3097 16 cr := Util.getOption(v.exportVar);
3098
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
16 DAE.CREF_IDENT(ident = nm, subscriptLst = {}) := cr;
3099
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if listMember(nm, seen) then
3100 ✗ fail();
3101 end if;
3102 8 seen := nm :: seen;
3103 16 then SimCode.FMI_TERMINAL(nm, "", false, {SimCode.FMI_TERMINAL_MEMBER(cr, nm, "signal")}) :: terminals;
3104 8 else terminals;
3105 end matchcontinue;
3106 end for;
3107 30 terminals := listReverse(terminals);
3108 end simplePortTerminals;
3109
3110 protected function connectorMemberOf
3111 "If the variable stems from a connector, return its terminal (connector instance)
3112 name, the connector type path (terminalKind), the isExpandable flag and the
3113 terminal member descriptor. variableKind is the FMI 3.0 connection-semantics
3114 kind: `inflow` for a flow member (Kirchhoff's law), `signal` otherwise (values
3115 intended to be equal across a connection) - NOT the variable causality, which
3116 is already in modelDescription.xml."
3117 input SimCodeVar.SimVar var;
3118 output Option<tuple<String, String, Boolean, SimCode.FmiTerminalMember>> result;
3119 algorithm
3120 result := matchcontinue var
3121 local
3122 DAE.ComponentRef cref;
3123 String tname, member, tkind, kind;
3124 Boolean isExp;
3125 case _ guard isSome(var.exportVar)
3126 algorithm
3127 5186 cref := Util.getOption(var.exportVar);
3128 5186 (tname, member, isExp, tkind) := crefConnectorSplit(cref);
3129 // flow connector member -> Kirchhoff (inflow); otherwise a `signal` whose
3130 // values are intended to be equal across a connection. The flow flag comes
3131 // from the BackendDAE connectorType captured in the SimVar (the connector
3132 // type stored in the cref keeps only the type path, not member attributes).
3133
2/2
✓ Branch 0 taken 1494 times.
✓ Branch 1 taken 662 times.
2156 kind := if var.isConnectorFlow then "inflow" else "signal";
3134
1/2
✓ Branch 1 taken 2156 times.
✗ Branch 2 not taken.
4312 then SOME((tname, tkind, isExp, SimCode.FMI_TERMINAL_MEMBER(cref, member, kind)));
3135 else NONE();
3136 end matchcontinue;
3137 end connectorMemberOf;
3138
3139 public function getFMI3VisualizationResource
3140 "The <name>_visual.xml resource -d=visxml exported, or \"\" if none. CodegenFMU3
3141 emits it as the OpenModelica <Visualization> vendor annotation."
3142 input SimCode.SimCode simCode;
3143 output String resource = "";
3144 protected
3145 String path;
3146 algorithm
3147
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 if Flags.isSet(Flags.VISUAL_XML) then
3148 ✗ path := simCode.fileNamePrefix + "_visual.xml";
3149 ✗ if System.regularFileExists(path) then
3150 ✗ resource := simCode.fileNamePrefix + "_visual.xml";
3151 end if;
3152 end if;
3153 end getFMI3VisualizationResource;
3154
3155 protected function crefConnectorSplit
3156 "Split a cref at its outermost connector-typed qualifier: returns the connector
3157 instance name (terminal), the remaining member path, the connector's
3158 isExpandable flag and the connector type path (for terminalKind). Fails if no
3159 qualifier has a connector type."
3160 input DAE.ComponentRef cref;
3161 output String terminalName;
3162 output String memberName;
3163 output Boolean isExpandable;
3164 output String terminalKind;
3165 algorithm
3166 (terminalName, memberName, isExpandable, terminalKind) := match cref
3167 local
3168 DAE.ComponentRef rest;
3169 DAE.Type ity;
3170 String id, innerT, innerM, innerK;
3171 Boolean isExp;
3172 // the outermost qualifier is itself a connector: bus.a -> terminal bus, member a
3173 case DAE.CREF_QUAL(ident = id, identType = ity, componentRef = rest)
3174 guard Types.isConnector(ity)
3175 2156 then (id, ComponentReference.crefStr(rest), connectorIsExpandable(ity),
3176 connectorTypePath(ity));
3177 // a non-connector qualifier wrapping a connector deeper in: comp.bus.a
3178 case DAE.CREF_QUAL(ident = id, componentRef = rest)
3179 algorithm
3180 7524 (innerT, innerM, isExp, innerK) := crefConnectorSplit(rest);
3181 3004 then (id + "." + innerT, innerM, isExp, innerK);
3182 end match;
3183 end crefConnectorSplit;
3184
3185 protected function connectorIsExpandable
3186 input DAE.Type ty;
3187 output Boolean isExpandable;
3188 algorithm
3189 isExpandable := match ty
3190 local Boolean b;
3191 case DAE.T_COMPLEX(complexClassType = ClassInf.CONNECTOR(isExpandable = b)) then b;
3192 case DAE.T_SUBTYPE_BASIC(complexClassType = ClassInf.CONNECTOR(isExpandable = b)) then b;
3193 else false;
3194 end match;
3195 end connectorIsExpandable;
3196
3197 protected function connectorTypePath
3198 "The connector type path (e.g. Modelica....Flange_a) used as the FMI 3.0
3199 terminalKind. Empty string if the path is not available."
3200 input DAE.Type ty;
3201 output String path;
3202 algorithm
3203 path := match ty
3204 local Absyn.Path p;
3205 2156 case DAE.T_COMPLEX(complexClassType = ClassInf.CONNECTOR(path = p)) then AbsynUtil.pathString(p);
3206 ✗ case DAE.T_SUBTYPE_BASIC(complexClassType = ClassInf.CONNECTOR(path = p)) then AbsynUtil.pathString(p);
3207 else "";
3208 end match;
3209 end connectorTypePath;
3210
3211 public function getFMI3Clocks
3212 "Collect the FMI 3.0 output clocks from the model's clocked partitions: each
3213 base clock becomes one <Clock> variable (causality output). The value
3214 reference lies in the clock base-type block, after reals/integers/booleans/
3215 strings/binaries, matching FMI3_CLOCK_VR_OFFSET in the generated code."
3216 input SimCode.SimCode simCode;
3217 output list<SimCode.FmiClock> clocks = {};
3218 protected
3219 Integer offset, i = 0;
3220 algorithm
3221 16 offset := getFMI3ClockVROffset(simCode.modelInfo);
3222
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 16 times.
17 for p in simCode.clockedPartitions loop
3223 1 clocks := makeFmiClock(p.baseClock, offset + i, i) :: clocks;
3224 1 i := i + 1;
3225 end for;
3226 16 clocks := listReverse(clocks);
3227 end getFMI3Clocks;
3228
3229 protected function getFMI3ClockVROffset
3230 "First value reference of the clock base-type block (after the real, integer,
3231 boolean, string and binary/external-object blocks)."
3232 input SimCode.ModelInfo modelInfo;
3233 output Integer offset;
3234 protected
3235 SimCodeVar.SimVars vars = modelInfo.vars;
3236 algorithm
3237 64 offset := 2*numScalarElems(vars.stateVars) + numScalarElems(vars.algVars) + numScalarElems(vars.discreteAlgVars) + numScalarElems(vars.paramVars) + numScalarElems(vars.aliasVars)
3238 + numScalarElems(vars.intAlgVars) + numScalarElems(vars.intParamVars) + numScalarElems(vars.intAliasVars)
3239 + numScalarElems(vars.boolAlgVars) + numScalarElems(vars.boolParamVars) + numScalarElems(vars.boolAliasVars)
3240 + numScalarElems(vars.stringAlgVars) + numScalarElems(vars.stringParamVars) + numScalarElems(vars.stringAliasVars)
3241 + numScalarElems(vars.extObjVars);
3242 end getFMI3ClockVROffset;
3243
3244 protected function makeFmiClock
3245 "Map an OpenModelica clock kind to an FMI 3.0 <Clock> descriptor."
3246 input DAE.ClockKind kind;
3247 input Integer vr;
3248 input Integer idx;
3249 output SimCode.FmiClock clk;
3250 protected
3251 String nm = "$clock" + intString(idx + 1);
3252 algorithm
3253 clk := match kind
3254 local DAE.Exp e, ic, res; String iv;
3255 // periodic real clock: constant interval if the period is a literal
3256 case DAE.REAL_CLOCK(interval = e)
3257 ✗ then SimCode.FMI_CLOCK(vr, nm, (if stringEq(clockConstString(e), "") then "fixed" else "constant"), false, clockConstString(e), "", "");
3258 // rational clock: counter/resolution fraction
3259 case DAE.RATIONAL_CLOCK(intervalCounter = ic, resolution = res)
3260 1 then SimCode.FMI_CLOCK(vr, nm, "constant", true, "", clockConstString(ic), clockConstString(res));
3261 // event clock: ticks when a condition becomes true
3262 case DAE.EVENT_CLOCK()
3263 ✗ then SimCode.FMI_CLOCK(vr, nm, "triggered", false, "", "", "");
3264 ✗ else SimCode.FMI_CLOCK(vr, nm, "fixed", false, "", "", "");
3265 end match;
3266 end makeFmiClock;
3267
3268 protected function clockConstString
3269 "The numeric value of a clock interval/counter expression as a string, or \"\"
3270 when it is not a literal constant."
3271 input DAE.Exp e;
3272 output String s;
3273 algorithm
3274 s := match e
3275 local Real r; Integer i;
3276 ✗ case DAE.RCONST(r) then realString(r);
3277 2 case DAE.ICONST(i) then intString(i);
3278 else "";
3279 end match;
3280 end clockConstString;
3281
3282 public function unbalancedEqSystemPartition
3283 input list<SimCode.SimEqSystem> inList;
3284 input Integer maxLength;
3285 output list<list<SimCode.SimEqSystem>> partitions;
3286 protected
3287 Integer length, eqLength;
3288 list<SimCode.SimEqSystem> lst, cur;
3289 SimCode.SimEqSystem first;
3290 algorithm
3291 lst := inList;
3292 cur := {};
3293 partitions := {};
3294 length := 0;
3295
2/2
✓ Branch 0 taken 1098 times.
✓ Branch 1 taken 1204 times.
2302 while not listEmpty(lst) loop
3296 1098 first::lst := lst;
3297 1098 eqLength := getNumContinuousEquationsSingleEq(first);
3298
3/4
✓ Branch 0 taken 851 times.
✓ Branch 1 taken 247 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 851 times.
1098 if length > 0 and length + eqLength > maxLength then
3299 partitions := cur :: partitions;
3300 length := 0;
3301 cur := {};
3302 end if;
3303 1098 length := eqLength + length;
3304 cur := first :: cur;
3305 end while;
3306
2/2
✓ Branch 0 taken 957 times.
✓ Branch 1 taken 247 times.
1204 if not listEmpty(cur) then
3307 partitions := cur :: partitions;
3308 end if;
3309 end unbalancedEqSystemPartition;
3310
3311 public function selectNLEqSys
3312 input list<SimCode.SimEqSystem> simEqSysIn;
3313 output list<SimCode.SimEqSystem> eqs;
3314 protected
3315 SimCode.SimEqSystem e;
3316 algorithm
3317
2/2
✓ Branch 0 taken 1098 times.
✓ Branch 1 taken 1204 times.
2302 eqs := list(match eq case SimCode.SES_NONLINEAR() then eq; case SimCode.SES_MIXED(cont=e as SimCode.SES_NONLINEAR()) then e; end match for eq guard match eq case SimCode.SES_NONLINEAR() then true; case SimCode.SES_MIXED(cont=SimCode.SES_NONLINEAR()) then true; else false; end match in simEqSysIn);
3318 end selectNLEqSys;
3319
3320 protected function matrixFormatC
3321 "The SOLVER_MATRIX_FORMAT for a system of this shape. An unknown count, which is
3322 any pattern only built at runtime, counts as dense."
3323 input Integer size;
3324 input Option<Integer> nnz;
3325 input Boolean isLinear;
3326 output String format;
3327 protected
3328 Integer entries = Util.getOptionOrDefault(nnz, size * size);
3329 algorithm
3330
2/2
✓ Branch 1 taken 2125 times.
✓ Branch 2 taken 29 times.
2154 format := if useSparseSolver(size, entries, isLinear) then "OMC_MATRIX_SPARSE" else "OMC_MATRIX_DENSE";
3331 end matrixFormatC;
3332
3333 public function linearSystemMatrixFormat
3334 "Format for this linear system, counting nonzeros off the Jacobian's sparsity
3335 where there is one, which is what the runtime used to measure itself."
3336 input SimCode.LinearSystem ls;
3337 output String format;
3338 protected
3339 Option<Integer> nnz;
3340 algorithm
3341 1053 nnz := sparsityNonzeros(ls.jacobianMatrix);
3342
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1053 times.
✓ Branch 2 taken 137 times.
✓ Branch 3 taken 916 times.
1053 if isNone(nnz) then
3343 // No sparsity info at all -- fall back to the simJac entry count.
3344 137 nnz := simJacNonzeros(ls.simJac);
3345 end if;
3346 1053 format := matrixFormatC(listLength(ls.vars), nnz, true);
3347 end linearSystemMatrixFormat;
3348
3349 public function nonlinearSystemMatrixFormat
3350 "Format for this nonlinear system."
3351 input SimCode.NonlinearSystem nls;
3352 output String format;
3353 algorithm
3354 format := match nls
3355 case SimCode.NONLINEARSYSTEM()
3356 1101 then matrixFormatC(listLength(nls.crefs), sparsityNonzeros(nls.jacobianMatrix), false);
3357 end match;
3358 end nonlinearSystemMatrixFormat;
3359
3360 protected function simJacNonzeros
3361 "Entries of A, which a torn system does not give elementwise."
3362 input list<tuple<Integer, Integer, SimCode.SimEqSystem>> simJac;
3363 output Option<Integer> nnz = if listEmpty(simJac) then NONE() else SOME(listLength(simJac));
3364 end simJacNonzeros;
3365
3366 protected function sparsityNonzeros
3367 "Entries of a Jacobian's sparsity pattern, unknown without one."
3368 input Option<SimCode.JacobianMatrix> ojac;
3369 output Option<Integer> nnz;
3370 protected
3371 Integer entries = 0;
3372 algorithm
3373 nnz := match ojac
3374 local
3375 SimCode.SparsityPattern sparsity;
3376 list<SimCode.SparsityRow> rows;
3377 case SOME(SimCode.JAC_MATRIX(sparsity = sparsity)) guard not listEmpty(sparsity) algorithm
3378
2/2
✓ Branch 0 taken 6509 times.
✓ Branch 1 taken 1757 times.
8266 for col in sparsity loop
3379 6509 entries := entries + listLength(Util.tuple22(col));
3380 end for;
3381 then SOME(entries);
3382 case SOME(SimCode.JAC_MATRIX(sparsityMatrix = SimCode.Sparsity.SPARSITY(rows = rows))) algorithm
3383
2/2
✓ Branch 0 taken 727 times.
✓ Branch 1 taken 85 times.
812 for row in rows loop
3384 727 entries := entries + listLength(row.dependencies);
3385 end for;
3386 then SOME(entries);
3387 else NONE();
3388 end match;
3389 end sparsityNonzeros;
3390
3391 public function getExpNominal
3392 "Returns the nominal value of an expression.
3393 Used to scale zero-crossings like `a > b`."
3394 input DAE.Exp expr;
3395 output DAE.Exp nominal;
3396 algorithm
3397 nominal := match expr
3398 local
3399 DAE.ComponentRef cr;
3400 SimCodeVar.SimVar v;
3401 Real r1, r2;
3402 DAE.Exp e1, e2;
3403 DAE.Type t;
3404
3405 // for const 0 use zero nominal to not saturate the rest of the expression
3406 ✗ case DAE.ICONST() then DAE.RCONST(abs(intReal(expr.integer)));
3407 1547 case DAE.RCONST() then DAE.RCONST(abs(expr.real));
3408
3409 // time is monotonic, a relation on it cannot chatter
3410 case DAE.CREF(componentRef = DAE.CREF_IDENT(ident = "time")) then DAE.RCONST(0.0);
3411
3412 case DAE.CREF(componentRef = cr, ty = t) algorithm
3413 4768 v := cref2simvar(cr, getSimCode());
3414 then match v.nominalValue
3415 295 case SOME(DAE.RCONST(r1)) then DAE.RCONST(abs(r1));
3416 24 case SOME(e1) then Expression.makePureBuiltinCall("abs", {e1}, t);
3417 case NONE() then match v.varKind
3418 // for parameters and discrete variables use their actual value
3419 1043 case BackendDAE.PARAM() then Expression.makePureBuiltinCall("abs", {expr}, t);
3420 53 case BackendDAE.DISCRETE() then Expression.makePureBuiltinCall("abs", {expr}, t);
3421 else DAE.RCONST(1.0);
3422 // TODO use min/max to deduce better nominal value than 1.
3423 end match;
3424 end match;
3425
3426 // a + b = (A*as) + (B*bs) = (A+B)*(A/(A+B)*as + B/(A+B)*bs)
3427 // FIXME if A = B and a and b have opposite signs then the nominal value of
3428 // a+b may be arbitrarily small, but it's definitely smaller than A+B
3429 case DAE.BINARY(operator = DAE.ADD())
3430 then match (getExpNominal(expr.exp1), getExpNominal(expr.exp2))
3431 20 case (DAE.RCONST(r1), DAE.RCONST(r2)) then DAE.RCONST(r1 + r2);
3432 141 case (e1, e2) then DAE.BINARY(e1, expr.operator, e2);
3433 end match;
3434
3435 // similar to DAE.ADD
3436 case DAE.BINARY(operator = DAE.SUB(ty = t))
3437 then match (getExpNominal(expr.exp1), getExpNominal(expr.exp2))
3438 24 case (DAE.RCONST(r1), DAE.RCONST(r2)) then DAE.RCONST(r1 + r2);
3439 104 case (e1, e2) then DAE.BINARY(e1, DAE.ADD(t), e2);
3440 end match;
3441
3442 // a*b = (A*as)*(B*bs) = (A*B)*(as*bs)
3443 case DAE.BINARY(operator = DAE.MUL())
3444 then match (getExpNominal(expr.exp1), getExpNominal(expr.exp2))
3445 17 case (DAE.RCONST(r1), DAE.RCONST(r2)) then DAE.RCONST(r1*r2);
3446 123 case (e1, e2) then DAE.BINARY(e1, expr.operator, e2);
3447 end match;
3448
3449 // a/b = (A*as)/(B*bs) = (A/B)*(as/bs)
3450 case DAE.BINARY(operator = DAE.DIV())
3451 then match (getExpNominal(expr.exp1), getExpNominal(expr.exp2))
3452
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
56 case (DAE.RCONST(r1), DAE.RCONST(r2)) then DAE.RCONST(r1/r2);
3453 ✗ case (e1, e2) then DAE.BINARY(e1, expr.operator, e2);
3454 end match;
3455
3456 // a^b = (A*as)^(B*bs) = (A^B)^bs * (as)^(B*bs)
3457 case DAE.BINARY(operator = DAE.POW())
3458 then match (getExpNominal(expr.exp1), getExpNominal(expr.exp2))
3459 ✗ case (DAE.RCONST(r1), DAE.RCONST(r2)) then DAE.RCONST(r1^r2);
3460 ✗ case (e1, e2) then DAE.BINARY(e1, expr.operator, e2);
3461 end match;
3462
3463 // -a = -(A*as) = A*(-as)
3464 case DAE.UNARY(operator = DAE.UMINUS())
3465 196 then getExpNominal(expr.exp);
3466
3467 // if cond then a else b = if cond then A*as else B*bs
3468 case DAE.IFEXP()
3469 ✗ then DAE.IFEXP(expr.expCond, getExpNominal(expr.expThen), getExpNominal(expr.expElse));
3470
3471 // |a| = |A*as| = A*|as|
3472 case DAE.CALL(path = Absyn.IDENT(name = "abs"), expLst = {e1})
3473 66 then getExpNominal(e1);
3474
3475 // sign has values {-1,0,1}
3476 case DAE.CALL(path = Absyn.IDENT(name = "sign"))
3477 then DAE.RCONST(1.0);
3478
3479 // sqrt(a) = sqrt(A*as) = sqrt(A)*sqrt(as)
3480 case DAE.CALL(path = Absyn.IDENT(name = "sqrt"), expLst = {e1}, attr = DAE.CALL_ATTR(ty = t))
3481 then match getExpNominal(e1)
3482 ✗ case DAE.RCONST(r1) then DAE.RCONST(sqrt(r1));
3483 ✗ case e2 then Expression.makePureBuiltinCall("sqrt", {e2}, t);
3484 end match;
3485
3486 // div(a, b) is approximately a/b as long as a >> b
3487 case DAE.CALL(path = Absyn.IDENT(name = "div"), expLst = {e1, e2})
3488 then match (getExpNominal(e1), getExpNominal(e2))
3489 ✗ case (DAE.RCONST(r1), DAE.RCONST(r2)) then DAE.RCONST(max(1.0, abs(r1 / r2)));
3490 else DAE.RCONST(1.0);
3491 end match;
3492
3493 // mod(a, b) has values in [0, b]
3494 case DAE.CALL(path = Absyn.IDENT(name = "mod"), expLst = {_, e2})
3495 then match getExpNominal(e2)
3496 ✗ case DAE.RCONST(r2) then DAE.RCONST(r2);
3497 else DAE.RCONST(1.0);
3498 end match;
3499
3500 // rem(a, b) has values in [-b, b]
3501 case DAE.CALL(path = Absyn.IDENT(name = "rem"), expLst = {_, e2})
3502 then match getExpNominal(e2)
3503 ✗ case DAE.RCONST(r2) then DAE.RCONST(r2);
3504 else DAE.RCONST(1.0);
3505 end match;
3506
3507 // ceil(a) is approximately a as long as a >> 0
3508 case DAE.CALL(path = Absyn.IDENT(name = "ceil"), expLst = {e1})
3509 ✗ then getExpNominal(e1);
3510
3511 // floor(a) is approximately a as long as a >> 0
3512 case DAE.CALL(path = Absyn.IDENT(name = "floor"), expLst = {e1})
3513 ✗ then getExpNominal(e1);
3514
3515 // sin(a) has values in [-1, 1]
3516 // TODO for a << 1, sin(a) is approximately a
3517 case DAE.CALL(path = Absyn.IDENT(name = "sin"))
3518 then DAE.RCONST(1.0);
3519
3520 // cos(a) has values in [-1, 1]
3521 case DAE.CALL(path = Absyn.IDENT(name = "cos"))
3522 then DAE.RCONST(1.0);
3523
3524 // NOTE: tan(a) is all over the place and proper scaling can be very hard
3525 // for a << 1, tan(a) is approximately a
3526 case DAE.CALL(path = Absyn.IDENT(name = "tan"), expLst = {e1})
3527 ✗ then getExpNominal(e1);
3528
3529 // for a << 1, asin(a) is approximately a
3530 case DAE.CALL(path = Absyn.IDENT(name = "asin"), expLst = {e1})
3531 ✗ then getExpNominal(e1);
3532
3533 // acos(a) has values in [0, pi]
3534 case DAE.CALL(path = Absyn.IDENT(name = "acos"))
3535 then DAE.RCONST(1.0);
3536
3537 // atan(a) has values in [-pi/2, pi/2]
3538 // TODO for a << 1, atan(a) is approximately a
3539 case DAE.CALL(path = Absyn.IDENT(name = "atan"))
3540 then DAE.RCONST(1.0);
3541
3542 // atan2(a,b) has values in [-pi, pi]
3543 case DAE.CALL(path = Absyn.IDENT(name = "atan"))
3544 then DAE.RCONST(1.0);
3545
3546 // for these just calculate the value
3547 // f(a) = f(A*as) = f(A + A*(as-1)) = f(A) + o(A*(as-1))
3548 case DAE.CALL(path = Absyn.IDENT(name = "sinh"), expLst = {e1}, attr = DAE.CALL_ATTR(ty = t))
3549 then match getExpNominal(e1)
3550 ✗ case DAE.RCONST(r1) then DAE.RCONST(sinh(r1));
3551 ✗ case e2 then Expression.makePureBuiltinCall("sinh", {e2}, t);
3552 end match;
3553
3554 case DAE.CALL(path = Absyn.IDENT(name = "cosh"), expLst = {e1}, attr = DAE.CALL_ATTR(ty = t))
3555 then match getExpNominal(e1)
3556 ✗ case DAE.RCONST(r1) then DAE.RCONST(cosh(r1));
3557 ✗ case e2 then Expression.makePureBuiltinCall("cosh", {e2}, t);
3558 end match;
3559
3560 case DAE.CALL(path = Absyn.IDENT(name = "tanh"), expLst = {e1}, attr = DAE.CALL_ATTR(ty = t))
3561 then match getExpNominal(e1)
3562 ✗ case DAE.RCONST(r1) then DAE.RCONST(tanh(r1));
3563 ✗ case e2 then Expression.makePureBuiltinCall("tanh", {e2}, t);
3564 end match;
3565
3566 // exp(a) = exp(A*as) = exp(A)^as
3567 case DAE.CALL(path = Absyn.IDENT(name = "exp"), expLst = {e1}, attr = DAE.CALL_ATTR(ty = t))
3568 then match getExpNominal(e1)
3569 ✗ case DAE.RCONST(r1) then DAE.RCONST(exp(r1));
3570 ✗ case e2 then Expression.makePureBuiltinCall("exp", {e2}, t);
3571 end match;
3572
3573 // log(a) = log(A*as) = log(A) + log(as)
3574 case DAE.CALL(path = Absyn.IDENT(name = "log"), expLst = {e1}, attr = DAE.CALL_ATTR(ty = t))
3575 then match getExpNominal(e1)
3576 ✗ case DAE.RCONST(r1) then DAE.RCONST(log(r1));
3577 ✗ case e2 then Expression.makePureBuiltinCall("log", {e2}, t);
3578 end match;
3579
3580 // log10(a) = log10(A*as) = log10(A) + log10(as)
3581 case DAE.CALL(path = Absyn.IDENT(name = "log10"), expLst = {e1}, attr = DAE.CALL_ATTR(ty = t))
3582 then match getExpNominal(e1)
3583 ✗ case DAE.RCONST(r1) then DAE.RCONST(log10(r1));
3584 ✗ case e2 then Expression.makePureBuiltinCall("log10", {e2}, t);
3585 end match;
3586
3587 else DAE.RCONST(1.0);
3588 end match;
3589 end getExpNominal;
3590
3591 public function simIteratorString
3592 input BackendDAE.SimIterator iter;
3593 output String str;
3594 algorithm
3595 str := match iter
3596 423 case BackendDAE.SIM_ITERATOR_RANGE() then ComponentReferenceBasics.printComponentRefStr(iter.name) + " in " + ExpressionBasics.printExpStr(iter.start) + ":" + ExpressionBasics.printExpStr(iter.step) + ":" + ExpressionBasics.printExpStr(iter.stop);
3597 6 case BackendDAE.SIM_ITERATOR_LIST() then ComponentReferenceBasics.printComponentRefStr(iter.name) + " in " + List.toString(iter.lst, intString, List.Style.FLAT_CURLY_SHORT);
3598 end match;
3599 end simIteratorString;
3600
3601 public function useSparseSolver
3602 "Whether a system of this size and sparsity is factorized sparse or dense. The
3603 runtime used to decide this itself, which left the backend guessing."
3604 input Integer size;
3605 input Integer nnz;
3606 input Boolean isLinear;
3607 output Boolean sparse;
3608 protected
3609 constant Real maxDensityLinear = 0.2, maxDensityNonlinear = 0.1;
3610 constant Integer minSize = 1000;
3611 algorithm
3612
7/10
✓ Branch 0 taken 4516 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4516 times.
✓ Branch 5 taken 1489 times.
✓ Branch 6 taken 3027 times.
✓ Branch 7 taken 4206 times.
✓ Branch 8 taken 310 times.
✓ Branch 9 taken 4206 times.
✗ Branch 10 not taken.
6005 sparse := if size <= 0 then false
3613 else intReal(nnz) / intReal(size * size) < (if isLinear then maxDensityLinear else maxDensityNonlinear)
3614 or size > minSize;
3615 end useSparseSolver;
3616
3617 annotation(__OpenModelica_Interface="codegen_util");
3618 end SimCodeCodegenUtil;
3619