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 |