OMCompiler/Compiler/NFFrontEnd/NFStreamFlowAlias.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 NFStreamFlowAlias | ||
| 37 | " file: NFStreamFlowAlias.mo | ||
| 38 | package: NFStreamFlowAlias | ||
| 39 | description: | ||
| 40 | |||
| 41 | " | ||
| 42 | import DisjointSets; | ||
| 43 | import Component = NFComponent; | ||
| 44 | import ComponentRef = NFComponentRef; | ||
| 45 | import DAE; | ||
| 46 | import ElementSource; | ||
| 47 | import Equation = NFEquation; | ||
| 48 | import Expression = NFExpression; | ||
| 49 | import FlatModel = NFFlatModel; | ||
| 50 | import NFInstNode.InstNode; | ||
| 51 | import NFPrefixes.Variability; | ||
| 52 | import Operator = NFOperator; | ||
| 53 | import Type = NFType; | ||
| 54 | |||
| 55 | protected | ||
| 56 | import Binding = NFBinding; | ||
| 57 | import Ceval = NFCeval; | ||
| 58 | import Structural = NFStructural; | ||
| 59 | import Variable = NFVariable; | ||
| 60 | import MetaModelica.Dangerous.listReverseInPlace; | ||
| 61 | |||
| 62 | public | ||
| 63 | |||
| 64 | extends DisjointSets(redeclare type Entry = FlowAlias); | ||
| 65 | |||
| 66 | uniontype FlowAlias | ||
| 67 | record FLOW_ALIAS | ||
| 68 | ComponentRef name; | ||
| 69 | Boolean negative; | ||
| 70 | Option<Variable> variable; | ||
| 71 | end FLOW_ALIAS; | ||
| 72 | |||
| 73 | function isNonFlow | ||
| 74 | input FlowAlias alias; | ||
| 75 | output Boolean isNonFlow = isSome(alias.variable) and not Variable.isFlow(Util.getOption(alias.variable)); | ||
| 76 | end isNonFlow; | ||
| 77 | end FlowAlias; | ||
| 78 | |||
| 79 | redeclare function extends EntryHash | ||
| 80 | algorithm | ||
| 81 | 241 | hash := ComponentRef.hash(entry.name); | |
| 82 | end EntryHash; | ||
| 83 | |||
| 84 | redeclare function extends EntryEqual | ||
| 85 | algorithm | ||
| 86 | // Only compare the names, not the signs, since an alias and its negative | ||
| 87 | // can't belong to different sets. | ||
| 88 | 87 | isEqual := ComponentRef.isEqual(entry1.name, entry2.name); | |
| 89 | end EntryEqual; | ||
| 90 | |||
| 91 | redeclare function extends EntryString | ||
| 92 | algorithm | ||
| 93 | ✗ | str := ComponentRef.toString(entry.name); | |
| 94 | ✗ | if entry.negative then | |
| 95 | ✗ | str := "-" + str; | |
| 96 | end if; | ||
| 97 | end EntryString; | ||
| 98 | |||
| 99 | type Replacements = UnorderedMap<ComponentRef, Expression>; | ||
| 100 | |||
| 101 | function eliminateAliases | ||
| 102 | "Performs alias eliminiation for flow variables defined in stream connectors." | ||
| 103 | input output FlatModel flatModel; | ||
| 104 | input UnorderedMap<ComponentRef, Variable> vars; | ||
| 105 | output Replacements replacements; | ||
| 106 | protected | ||
| 107 | Sets sets; | ||
| 108 | list<tuple<FlowAlias, list<FlowAlias>>> aliases; | ||
| 109 | algorithm | ||
| 110 | 8 | (flatModel, sets) := fromModel(flatModel); | |
| 111 | 8 | (flatModel, aliases) := createAliases(sets, vars, flatModel); | |
| 112 | 7 | replacements := buildReplacements(aliases); | |
| 113 | 7 | flatModel := applyReplacements(replacements, flatModel); | |
| 114 | 7 | findConstantBindings(flatModel, vars); | |
| 115 | end eliminateAliases; | ||
| 116 | |||
| 117 | function fromModel | ||
| 118 | "Returns the alias sets and equations found in the given model, | ||
| 119 | removing the alias equations from the model in the process." | ||
| 120 | input output FlatModel flatModel; | ||
| 121 | output Sets sets; | ||
| 122 | protected | ||
| 123 | list<Equation> alias_eqs, other_eqs; | ||
| 124 | list<list<FlowAlias>> flow_aliases; | ||
| 125 | list<Variable> vars = {}; | ||
| 126 | Option<FlowAlias> opt_alias; | ||
| 127 | FlowAlias alias; | ||
| 128 | algorithm | ||
| 129 | 8 | sets := emptySets(0); | |
| 130 | |||
| 131 | // Find alias equations and add them to the alias sets. | ||
| 132 | 8 | (alias_eqs, flow_aliases, other_eqs) := sortEquations(flatModel.equations); | |
| 133 | 8 | flatModel.equations := other_eqs; | |
| 134 | 8 | sets := List.threadFold(flow_aliases, alias_eqs, addAliasEquation, sets); | |
| 135 | |||
| 136 | // Find alias binding equations and add them to the alias sets. | ||
| 137 | 8 | sets := List.fold(flatModel.variables, addAliasBinding, sets); | |
| 138 | |||
| 139 | // Update the aliases with which Variable they're associated with. | ||
| 140 |
2/2✓ Branch 0 taken 127 times.
✓ Branch 1 taken 8 times.
|
135 | for v in flatModel.variables loop |
| 141 | 127 | alias := FlowAlias.FLOW_ALIAS(v.name, false, NONE()); | |
| 142 | 127 | opt_alias := getEntry(alias, sets); | |
| 143 | |||
| 144 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
✓ Branch 2 taken 100 times.
✓ Branch 3 taken 27 times.
|
127 | if isSome(opt_alias) then |
| 145 | 27 | SOME(alias) := opt_alias; | |
| 146 | 27 | alias.variable := SOME(v); | |
| 147 | 27 | UnorderedMap.updateKey(alias, sets.elements); | |
| 148 | else | ||
| 149 | vars := v :: vars; | ||
| 150 | end if; | ||
| 151 | end for; | ||
| 152 | |||
| 153 | // Sanity check, all aliases should have an associated Variable now. | ||
| 154 |
2/2✓ Branch 1 taken 27 times.
✓ Branch 2 taken 8 times.
|
35 | for alias in UnorderedMap.keyList(sets.elements) loop |
| 155 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 27 times.
|
27 | if isNone(alias.variable) then |
| 156 | ✗ | Error.addInternalError(getInstanceName() + ": " + ComponentRef.toString(alias.name) + | |
| 157 | " has no associated variable", sourceInfo()); | ||
| 158 | end if; | ||
| 159 | end for; | ||
| 160 | |||
| 161 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | flatModel.variables := MetaModelica.Dangerous.listReverseInPlace(vars); |
| 162 | end fromModel; | ||
| 163 | |||
| 164 | function sortEquations | ||
| 165 | "Sorts a list of equations into alias equations and non-alias equations, | ||
| 166 | and also returns the aliases found in each alias equation." | ||
| 167 | input list<Equation> eqs; | ||
| 168 | output list<Equation> aliasEqs = {}; | ||
| 169 | output list<list<FlowAlias>> flowAliases = {}; | ||
| 170 | output list<Equation> otherEqs = {}; | ||
| 171 | protected | ||
| 172 | list<FlowAlias> aliases; | ||
| 173 | DAE.ElementSource src; | ||
| 174 | algorithm | ||
| 175 |
2/2✓ Branch 0 taken 95 times.
✓ Branch 1 taken 8 times.
|
103 | for eq in eqs loop |
| 176 | 95 | aliases := getAliasVarsFromEq(eq); | |
| 177 | |||
| 178 |
2/2✓ Branch 0 taken 80 times.
✓ Branch 1 taken 15 times.
|
95 | if listEmpty(aliases) then |
| 179 | otherEqs := eq :: otherEqs; | ||
| 180 | else | ||
| 181 | 15 | src := Equation.source(eq); | |
| 182 | 15 | src := ElementSource.addAdditionalComment(src, "alias equation"); | |
| 183 | 15 | eq := Equation.setSource(src, eq); | |
| 184 | aliasEqs := eq :: aliasEqs; | ||
| 185 | flowAliases := aliases :: flowAliases; | ||
| 186 | end if; | ||
| 187 | end for; | ||
| 188 | |||
| 189 | 8 | aliasEqs := MetaModelica.Dangerous.listReverseInPlace(aliasEqs); | |
| 190 | 8 | flowAliases := MetaModelica.Dangerous.listReverseInPlace(flowAliases); | |
| 191 | 8 | otherEqs := MetaModelica.Dangerous.listReverseInPlace(otherEqs); | |
| 192 | end sortEquations; | ||
| 193 | |||
| 194 | function addAliasEquation | ||
| 195 | "Adds an alias equation to the sets." | ||
| 196 | input list<FlowAlias> aliases; | ||
| 197 | input Equation eq; | ||
| 198 | input output Sets sets; | ||
| 199 | protected | ||
| 200 | list<FlowAlias> scalar_aliases1, scalar_aliases2; | ||
| 201 | FlowAlias alias1, alias2; | ||
| 202 | algorithm | ||
| 203 |
3/6✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 15 times.
|
15 | {alias1, alias2} := aliases; |
| 204 | |||
| 205 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
|
15 | if Equation.isArrayEquality(eq) then |
| 206 | ✗ | scalar_aliases1 := scalarizeAlias(alias1); | |
| 207 | ✗ | scalar_aliases2 := scalarizeAlias(alias2); | |
| 208 | ✗ | sets := List.threadFold(scalar_aliases1, scalar_aliases2, addAliasPair, sets); | |
| 209 | else | ||
| 210 | 15 | sets := addAliasPair(alias1, alias2, sets); | |
| 211 | end if; | ||
| 212 | end addAliasEquation; | ||
| 213 | |||
| 214 | function addAliasBinding | ||
| 215 | "Adds a variable's binding equation to the sets if var = binding qualifies | ||
| 216 | as an alias equation." | ||
| 217 | input Variable var; | ||
| 218 | input output Sets sets; | ||
| 219 | protected | ||
| 220 | Expression bind_exp; | ||
| 221 | list<FlowAlias> aliases; | ||
| 222 | FlowAlias alias1, alias2; | ||
| 223 | algorithm | ||
| 224 |
2/2✓ Branch 1 taken 100 times.
✓ Branch 2 taken 27 times.
|
127 | if Binding.hasExp(var.binding) then |
| 225 | 27 | bind_exp := Binding.getExp(var.binding); | |
| 226 | 27 | aliases := getAliasVarsFromExpPair(Expression.fromTypedCref(var.name, var.ty), bind_exp); | |
| 227 | |||
| 228 |
1/2✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
|
27 | if not listEmpty(aliases) then |
| 229 | ✗ | {alias1, alias2} := aliases; | |
| 230 | ✗ | sets := addAliasPair(alias1, alias2, sets); | |
| 231 | end if; | ||
| 232 | end if; | ||
| 233 | end addAliasBinding; | ||
| 234 | |||
| 235 | function addAliasPair | ||
| 236 | "Adds the alias equation alias1 = alias2 to the sets." | ||
| 237 | input FlowAlias alias1; | ||
| 238 | input FlowAlias alias2; | ||
| 239 | input output Sets sets; | ||
| 240 | protected | ||
| 241 | Integer set1, set2, root1, root2; | ||
| 242 | Boolean flipped_sign1, flipped_sign2; | ||
| 243 | |||
| 244 | function find_set | ||
| 245 | "Returns the set that the alias or its negative belongs to, | ||
| 246 | as well as if it was the alias or its negative that was found." | ||
| 247 | input FlowAlias alias; | ||
| 248 | output Integer set; | ||
| 249 | input output Sets sets; | ||
| 250 | output Boolean flippedSign; | ||
| 251 | protected | ||
| 252 | FlowAlias entry; | ||
| 253 | algorithm | ||
| 254 | 30 | (set, sets) := findSet(alias, sets); | |
| 255 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 30 times.
|
30 | SOME(entry) := getEntry(alias, sets); |
| 256 |
1/2✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
|
30 | flippedSign := entry.negative <> alias.negative; |
| 257 | end find_set; | ||
| 258 | algorithm | ||
| 259 | // Find the sets the aliases or their negatives belong to. | ||
| 260 | 15 | (set1, sets, flipped_sign1) := find_set(alias1, sets); | |
| 261 | 15 | (set2, sets, flipped_sign2) := find_set(alias2, sets); | |
| 262 | |||
| 263 | // If the negative of exactly one of the aliases was found, then all the | ||
| 264 | // aliases in one of the sets need to be negated before merging the sets. | ||
| 265 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 13 times.
|
15 | if flipped_sign1 <> flipped_sign2 then |
| 266 | // TODO: If one of them got added as a new set, then we only need to flip that one. | ||
| 267 | 2 | root1 := findRoot(set1, sets.nodes); | |
| 268 | 2 | root2 := findRoot(set2, sets.nodes); | |
| 269 | |||
| 270 | // If both aliases belong to the same set then there are inconsistent | ||
| 271 | // alias equations (e.g. a = b, a = -b). | ||
| 272 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if root1 == root2 then |
| 273 | // TODO: Give an error message instead? | ||
| 274 | ✗ | return; | |
| 275 | end if; | ||
| 276 | |||
| 277 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | sets := negateSet(if alias1.negative then root1 else root2, sets); |
| 278 | end if; | ||
| 279 | |||
| 280 | 15 | sets := union(set1, set2, sets); | |
| 281 | end addAliasPair; | ||
| 282 | |||
| 283 | function getAliasVarsFromEq | ||
| 284 | "Returns the alias variables in an equation. For aliases to be found the | ||
| 285 | equation should be a simple equation such as a = b or a = -b, and at least | ||
| 286 | one of the variables should be a flow variable inside a stream connector." | ||
| 287 | input Equation eq; | ||
| 288 | output list<FlowAlias> aliases = {}; | ||
| 289 | algorithm | ||
| 290 | aliases := match eq | ||
| 291 | 95 | case Equation.EQUALITY() then getAliasVarsFromExpPair(eq.lhs, eq.rhs); | |
| 292 | else {}; | ||
| 293 | end match; | ||
| 294 | end getAliasVarsFromEq; | ||
| 295 | |||
| 296 | function getAliasVarsFromExpPair | ||
| 297 | "Returns the alias variables found in the given expressions if exp1 = exp2 | ||
| 298 | qualifies as an alias equation." | ||
| 299 | input Expression exp1; | ||
| 300 | input Expression exp2; | ||
| 301 | output list<FlowAlias> aliases; | ||
| 302 | algorithm | ||
| 303 | 122 | aliases := getAliasVarsFromExp(exp1, exp2, {}); | |
| 304 | 122 | aliases := getAliasVarsFromExp(exp2, exp1, aliases); | |
| 305 | |||
| 306 |
4/4✓ Branch 1 taken 33 times.
✓ Branch 2 taken 89 times.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 18 times.
|
122 | if listLength(aliases) <> 2 or List.none(aliases, isStreamConnectorFlow) then |
| 307 | aliases := {}; | ||
| 308 | end if; | ||
| 309 | end getAliasVarsFromExpPair; | ||
| 310 | |||
| 311 | function getAliasVarsFromExp | ||
| 312 | "Returns the alias variables for one side of an equation, with the other | ||
| 313 | side being used to check for e.g. zero equality but not contributing to | ||
| 314 | the list of aliases." | ||
| 315 | input Expression exp; | ||
| 316 | input Expression otherExp; | ||
| 317 | input output list<FlowAlias> aliases; | ||
| 318 | protected | ||
| 319 | Expression e, e1, e2; | ||
| 320 | algorithm | ||
| 321 | aliases := match exp | ||
| 322 | // a | ||
| 323 | case Expression.CREF() | ||
| 324 | guard ComponentRef.nodeVariability(exp.cref) > Variability.DISCRETE | ||
| 325 | 113 | then FlowAlias.FLOW_ALIAS(exp.cref, false, NONE()) :: aliases; | |
| 326 | |||
| 327 | // -a | ||
| 328 | case Expression.UNARY(exp = e as Expression.CREF()) | ||
| 329 | guard ComponentRef.nodeVariability(e.cref) > Variability.DISCRETE | ||
| 330 | 13 | then FlowAlias.FLOW_ALIAS(e.cref, true, NONE()) :: aliases; | |
| 331 | |||
| 332 | // a + b = 0 => a = -b; | ||
| 333 | case Expression.BINARY(operator = Operator.OPERATOR(op = NFOperator.Op.ADD)) | ||
| 334 | guard Expression.isZero(otherExp) | ||
| 335 | 1 | then getAliasVarsFromSum(exp.exp1, false, exp.exp2, false, aliases); | |
| 336 | |||
| 337 | // the same sum as an n-ary node, with the subtracted terms in inv_arguments | ||
| 338 | case Expression.MULTARY() | ||
| 339 | guard Expression.isZero(otherExp) and | ||
| 340 | Operator.getMathClassification(exp.operator) == NFOperator.MathClassification.ADDITION | ||
| 341 | then match (exp.arguments, exp.inv_arguments) | ||
| 342 | 6 | case ({e1, e2}, {}) then getAliasVarsFromSum(e1, false, e2, false, aliases); | |
| 343 | ✗ | case ({e1}, {e2}) then getAliasVarsFromSum(e1, false, e2, true, aliases); | |
| 344 | ✗ | case ({}, {e1, e2}) then getAliasVarsFromSum(e1, true, e2, true, aliases); | |
| 345 | else aliases; | ||
| 346 | end match; | ||
| 347 | |||
| 348 | else aliases; | ||
| 349 | end match; | ||
| 350 | end getAliasVarsFromExp; | ||
| 351 | |||
| 352 | function getAliasVarsFromSum | ||
| 353 | "Returns the alias variables for a two-term sum that is equated to zero. | ||
| 354 | inv1/inv2 tell whether the respective term is subtracted rather than added." | ||
| 355 | input Expression exp1; | ||
| 356 | input Boolean inv1; | ||
| 357 | input Expression exp2; | ||
| 358 | input Boolean inv2; | ||
| 359 | input output list<FlowAlias> aliases; | ||
| 360 | protected | ||
| 361 | list<FlowAlias> aliases1, aliases2; | ||
| 362 | FlowAlias alias1, alias2; | ||
| 363 | algorithm | ||
| 364 | 7 | aliases1 := getAliasVarsFromExp(exp1, exp2, {}); | |
| 365 | 7 | aliases2 := getAliasVarsFromExp(exp2, exp1, {}); | |
| 366 | |||
| 367 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
|
7 | if listLength(aliases1) == 1 and listLength(aliases2) == 1 then |
| 368 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
|
7 | {alias1} := aliases1; |
| 369 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
|
7 | {alias2} := aliases2; |
| 370 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
|
12 | alias1.negative := alias1.negative <> inv1; |
| 371 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
|
10 | alias2.negative := not (alias2.negative <> inv2); |
| 372 | aliases := alias1 :: alias2 :: aliases; | ||
| 373 | end if; | ||
| 374 | end getAliasVarsFromSum; | ||
| 375 | |||
| 376 | function isStreamConnectorFlow | ||
| 377 | "Checks if the given flow alias refers to a flow variable inside a stream connector." | ||
| 378 | input FlowAlias alias; | ||
| 379 | output Boolean isStreamFlow; | ||
| 380 | protected | ||
| 381 | InstNode node; | ||
| 382 | algorithm | ||
| 383 | // A top-level component is by definition not inside a stream connector. | ||
| 384 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 59 times.
|
59 | if not ComponentRef.isQualified(alias.name) then |
| 385 | isStreamFlow := false; | ||
| 386 | ✗ | return; | |
| 387 | end if; | ||
| 388 | |||
| 389 | 59 | node := ComponentRef.node(alias.name); | |
| 390 | |||
| 391 | // The component must be a flow variable. | ||
| 392 |
3/4✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 44 times.
✓ Branch 6 taken 15 times.
|
59 | if not InstNode.isComponent(node) or not Component.isFlow(InstNode.component(node)) then |
| 393 | isStreamFlow := false; | ||
| 394 | 44 | return; | |
| 395 | end if; | ||
| 396 | |||
| 397 | // The component must be inside a stream connector. | ||
| 398 | 15 | isStreamFlow := Type.isStreamConnector(ComponentRef.nodeType(ComponentRef.rest(alias.name))); | |
| 399 | end isStreamConnectorFlow; | ||
| 400 | |||
| 401 | function scalarizeAlias | ||
| 402 | input FlowAlias alias; | ||
| 403 | output list<FlowAlias> scalarAliases = {}; | ||
| 404 | protected | ||
| 405 | list<ComponentRef> crefs; | ||
| 406 | algorithm | ||
| 407 | ✗ | crefs := ComponentRef.scalarize(alias.name, false); | |
| 408 | ✗ | crefs := MetaModelica.Dangerous.listReverseInPlace(crefs); | |
| 409 | |||
| 410 | ✗ | for cr in crefs loop | |
| 411 | ✗ | scalarAliases := FLOW_ALIAS(cr, alias.negative, NONE()) :: scalarAliases; | |
| 412 | end for; | ||
| 413 | end scalarizeAlias; | ||
| 414 | |||
| 415 | function negateSet | ||
| 416 | "Flips the sign of all aliases in a set." | ||
| 417 | input Integer set; | ||
| 418 | input output Sets sets; | ||
| 419 | protected | ||
| 420 | array<Integer> nodes, indices; | ||
| 421 | UnorderedMap<FlowAlias, Integer> elements; | ||
| 422 | Integer root; | ||
| 423 | FlowAlias alias; | ||
| 424 | algorithm | ||
| 425 | 2 | nodes := sets.nodes; | |
| 426 | 2 | elements := sets.elements; | |
| 427 | 2 | root := findRoot(set, nodes); | |
| 428 | 2 | indices := UnorderedMap.valueArray(elements); | |
| 429 | |||
| 430 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
10 | for i in 1:arrayLength(indices) loop |
| 431 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
|
8 | if findRoot(i, nodes) == root then |
| 432 | 2 | alias := UnorderedMap.keyAt(elements, i); | |
| 433 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | alias.negative := not alias.negative; |
| 434 | 2 | Vector.updateNoBounds(elements.keys, i, alias); | |
| 435 | end if; | ||
| 436 | end for; | ||
| 437 | end negateSet; | ||
| 438 | |||
| 439 | function createAliases | ||
| 440 | "Extracts the alias sets and defines the representatives and their aliases." | ||
| 441 | input Sets sets; | ||
| 442 | input UnorderedMap<ComponentRef, Variable> vars; | ||
| 443 | input output FlatModel flatModel; | ||
| 444 | output list<tuple<FlowAlias, list<FlowAlias>>> aliases = {}; | ||
| 445 | protected | ||
| 446 | array<list<FlowAlias>> extracted_sets; | ||
| 447 | FlowAlias representative; | ||
| 448 | Variable repr_var; | ||
| 449 | list<FlowAlias> rest_aliases; | ||
| 450 | Binding repr_binding; | ||
| 451 | list<Variable> alias_vars = {}; | ||
| 452 | list<Equation> alias_eqs = {}; | ||
| 453 | Boolean negated; | ||
| 454 | algorithm | ||
| 455 | 8 | extracted_sets := extractSets(sets); | |
| 456 | |||
| 457 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 7 times.
|
19 | for set in extracted_sets loop |
| 458 | // Define the representative for the alias set. | ||
| 459 | 12 | (representative, rest_aliases) := defineRepresentative(set); | |
| 460 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
11 | SOME(repr_var) := representative.variable; |
| 461 | alias_vars := repr_var :: alias_vars; | ||
| 462 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
|
11 | true := UnorderedMap.tryUpdate(repr_var.name, repr_var, vars); |
| 463 | |||
| 464 | // Create a '= representative' binding for the aliases, to show which | ||
| 465 | // variable they're aliases of. | ||
| 466 | 11 | repr_binding := Variable.asBinding(repr_var); | |
| 467 | |||
| 468 | // Define the rest of the set as aliases of the representative. | ||
| 469 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 11 times.
|
25 | for alias in rest_aliases loop |
| 470 | 14 | negated := representative.negative <> alias.negative; | |
| 471 | 14 | (alias, alias_eqs) := defineAlias(alias, repr_binding, negated, alias_eqs); | |
| 472 | 14 | alias_vars := Util.getOption(alias.variable) :: alias_vars; | |
| 473 | end for; | ||
| 474 | |||
| 475 | 11 | aliases := (representative, rest_aliases) :: aliases; | |
| 476 | end for; | ||
| 477 | |||
| 478 | 7 | aliases := listReverseInPlace(aliases); | |
| 479 | 7 | flatModel.variables := listAppend(flatModel.variables, listReverseInPlace(alias_vars)); | |
| 480 | flatModel.equations := listAppend(flatModel.equations, listReverseInPlace(alias_eqs)); | ||
| 481 | end createAliases; | ||
| 482 | |||
| 483 | function buildReplacements | ||
| 484 | "Constructs an alias->representative map." | ||
| 485 | input list<tuple<FlowAlias, list<FlowAlias>>> aliases; | ||
| 486 | output Replacements replacements; | ||
| 487 | protected | ||
| 488 | FlowAlias representative; | ||
| 489 | Expression exp, negative_exp; | ||
| 490 | list<FlowAlias> rest_aliases; | ||
| 491 | algorithm | ||
| 492 | 7 | replacements := UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual); | |
| 493 | |||
| 494 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 7 times.
|
18 | for set in aliases loop |
| 495 | 11 | (representative, rest_aliases) := set; | |
| 496 | 11 | exp := Expression.fromCref(representative.name); | |
| 497 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3 times.
|
11 | exp := if representative.negative then Expression.negate(exp) else exp; |
| 498 | 11 | negative_exp := Expression.negate(exp); | |
| 499 | |||
| 500 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 11 times.
|
25 | for alias in rest_aliases loop |
| 501 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
|
25 | UnorderedMap.addUnique(alias.name, if alias.negative then negative_exp else exp, replacements); |
| 502 | end for; | ||
| 503 | end for; | ||
| 504 | end buildReplacements; | ||
| 505 | |||
| 506 | function applyReplacements | ||
| 507 | "Replaces aliases with the chosen representative variable in the model." | ||
| 508 | input Replacements replacements; | ||
| 509 | input output FlatModel flatModel; | ||
| 510 | algorithm | ||
| 511 | 7 | flatModel := FlatModel.mapExp(flatModel, function applyReplacementsInExp(replacements = replacements)); | |
| 512 | end applyReplacements; | ||
| 513 | |||
| 514 | function applyReplacementsInEql | ||
| 515 | input Replacements replacements; | ||
| 516 | input output list<Equation> eql; | ||
| 517 | algorithm | ||
| 518 | 7 | eql := Equation.mapExpList(eql, function applyReplacementsInExp(replacements = replacements)); | |
| 519 | end applyReplacementsInEql; | ||
| 520 | |||
| 521 | function applyReplacementsInExp | ||
| 522 | input Replacements replacements; | ||
| 523 | input Expression exp; | ||
| 524 | output Expression outExp = Expression.map(exp, function applyReplacementsInExp_traverser(replacements = replacements)); | ||
| 525 | end applyReplacementsInExp; | ||
| 526 | |||
| 527 | function applyReplacementsInExp_traverser | ||
| 528 | input Replacements replacements; | ||
| 529 | input output Expression exp; | ||
| 530 | protected | ||
| 531 | Option<Expression> opt_val; | ||
| 532 | algorithm | ||
| 533 | exp := match exp | ||
| 534 | case Expression.CREF() | ||
| 535 | algorithm | ||
| 536 | 367 | opt_val := UnorderedMap.get(exp.cref, replacements); | |
| 537 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 367 times.
✓ Branch 2 taken 40 times.
✓ Branch 3 taken 327 times.
|
367 | then |
| 538 | if isSome(opt_val) then Util.getOption(opt_val) else exp; | ||
| 539 | |||
| 540 | else exp; | ||
| 541 | end match; | ||
| 542 | end applyReplacementsInExp_traverser; | ||
| 543 | |||
| 544 | function defineRepresentative | ||
| 545 | "Defines a representative for an alias set, by choosing one of the aliases | ||
| 546 | as the representative and determining the start/nominal/min/max attributes | ||
| 547 | for it." | ||
| 548 | input list<FlowAlias> aliases; | ||
| 549 | output FlowAlias representative; | ||
| 550 | output list<FlowAlias> restAliases; | ||
| 551 | protected | ||
| 552 | list<tuple<ComponentRef, Binding>> start_values = {}, fixed_start_values = {}, nominal_values = {}; | ||
| 553 | list<Expression> min_values = {}, max_values = {}; | ||
| 554 | list<FlowAlias> accum_aliases = {}; | ||
| 555 | Binding start_binding, nominal_binding, min_binding, max_binding; | ||
| 556 | Boolean negated; | ||
| 557 | algorithm | ||
| 558 | try | ||
| 559 | // Try to select a non-flow variable as the representative for the set. | ||
| 560 | 12 | (representative, restAliases) := List.findAndRemove(aliases, FlowAlias.isNonFlow); | |
| 561 | else | ||
| 562 | // If there aren't any non-flow variables, just select any variable. | ||
| 563 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | representative :: restAliases := aliases; |
| 564 | end try; | ||
| 565 | |||
| 566 | // Evaluate the start/nominal/min/max attributes of the aliases and sort them into lists. | ||
| 567 |
2/2✓ Branch 1 taken 27 times.
✓ Branch 2 taken 12 times.
|
51 | for alias in representative :: restAliases loop |
| 568 | 27 | negated := representative.negative <> alias.negative; | |
| 569 | 27 | (alias, start_values, fixed_start_values, nominal_values, min_values, max_values) := | |
| 570 | evalAliasAttributes(alias, negated, start_values, fixed_start_values, nominal_values, min_values, max_values); | ||
| 571 | accum_aliases := alias :: accum_aliases; | ||
| 572 | end for; | ||
| 573 | |||
| 574 | // Compute start/nominal/min/max attributes for the representative. | ||
| 575 | // start/nominal is chosen according to 8.6.2 | ||
| 576 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 time.
|
12 | if listEmpty(fixed_start_values) then |
| 577 | 11 | start_binding := selectValue(start_values); | |
| 578 | else | ||
| 579 | // Prefer fixed start values over non-fixed. | ||
| 580 | 1 | start_binding := selectFixedStartValue(fixed_start_values); | |
| 581 | end if; | ||
| 582 | |||
| 583 | 11 | nominal_binding := selectValue(nominal_values); | |
| 584 | |||
| 585 | // min/max is max(min_values) and min(max_values) respectively. | ||
| 586 | 11 | min_binding := computeLimit(min_values, Ceval.evalBuiltinMax2); | |
| 587 | 11 | max_binding := computeLimit(max_values, Ceval.evalBuiltinMin2); | |
| 588 | |||
| 589 | // Update the representative with the new attributes. | ||
| 590 | 11 | representative := setRepresentativeAttributes(representative, start_binding, nominal_binding, | |
| 591 | min_binding, max_binding); | ||
| 592 | end defineRepresentative; | ||
| 593 | |||
| 594 | function computeLimit | ||
| 595 | "Computes a min or max value for a representative, depending on the reduce function." | ||
| 596 | input list<Expression> values; | ||
| 597 | input ReduceFn reduceFn; | ||
| 598 | output Binding limit; | ||
| 599 | protected | ||
| 600 | partial function ReduceFn | ||
| 601 | input Expression exp1; | ||
| 602 | input Expression exp2; | ||
| 603 | output Expression result; | ||
| 604 | end ReduceFn; | ||
| 605 | |||
| 606 | Expression res; | ||
| 607 | algorithm | ||
| 608 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 17 times.
|
22 | if listEmpty(values) then |
| 609 | limit := NFBinding.EMPTY_BINDING; | ||
| 610 | else | ||
| 611 | 5 | res := List.reduce(values, reduceFn); | |
| 612 | 5 | limit := Binding.makeFlat(res, Variability.CONSTANT, NFBinding.Source.GENERATED); | |
| 613 | end if; | ||
| 614 | end computeLimit; | ||
| 615 | |||
| 616 | function selectValue | ||
| 617 | "Returns the binding with the highest confidence. In case of a tie, the | ||
| 618 | binding belonging to the variable highest in the instance tree is chosen." | ||
| 619 | input list<tuple<ComponentRef, Binding>> bindings; | ||
| 620 | output Binding value = NFBinding.EMPTY_BINDING; | ||
| 621 | protected | ||
| 622 | Integer confidence, max_confidence = -1, name_len, cur_name_len = 0; | ||
| 623 | ComponentRef name; | ||
| 624 | Binding binding; | ||
| 625 | algorithm | ||
| 626 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | for b in bindings loop |
| 627 | ✗ | (name, binding) := b; | |
| 628 | ✗ | confidence := Binding.actualConfidence(binding); | |
| 629 | |||
| 630 | ✗ | if max_confidence < 0 or confidence < max_confidence then | |
| 631 | // Use this binding if it's the first or it has higher confidence than | ||
| 632 | // the current best. | ||
| 633 | value := binding; | ||
| 634 | ✗ | cur_name_len := ComponentRef.depth(name); | |
| 635 | max_confidence := confidence; | ||
| 636 | elseif confidence == max_confidence then | ||
| 637 | // Use this binding if it has the same confidence as the current best | ||
| 638 | // but it belongs to a variable higher up in the instance tree. | ||
| 639 | ✗ | name_len := ComponentRef.depth(name); | |
| 640 | ✗ | if name_len < cur_name_len then | |
| 641 | value := binding; | ||
| 642 | cur_name_len := name_len; | ||
| 643 | end if; | ||
| 644 | end if; | ||
| 645 | end for; | ||
| 646 | end selectValue; | ||
| 647 | |||
| 648 | function selectFixedStartValue | ||
| 649 | "Selects a start value from a list of fixed start values. | ||
| 650 | The start values have to be equal, otherwise it's an error." | ||
| 651 | input list<tuple<ComponentRef, Binding>> fixedBindings; | ||
| 652 | output Binding value = NFBinding.EMPTY_BINDING; | ||
| 653 | protected | ||
| 654 | list<Binding> bindings; | ||
| 655 | String str; | ||
| 656 | ComponentRef cref; | ||
| 657 | Binding binding; | ||
| 658 | algorithm | ||
| 659 |
4/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 time.
|
3 | bindings := list(Util.tuple22(b) for b in fixedBindings); |
| 660 | |||
| 661 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
|
1 | if List.allEqual(bindings, Binding.isEqual) then |
| 662 | // If all bindings are equal, then select any of them. | ||
| 663 | ✗ | value := listHead(bindings); | |
| 664 | else | ||
| 665 | // Otherwise print an error and fail. | ||
| 666 |
1/2✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
|
1 | if Flags.isSet(Flags.ALIAS_CONFLICTS) then |
| 667 | str := "Conflicting start values for fixed states:\n"; | ||
| 668 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
|
3 | for b in fixedBindings loop |
| 669 | 2 | (cref, binding) := b; | |
| 670 | 2 | str := str + " * Candidate: " + ComponentRef.toString(cref) + | |
| 671 | "(start = " + Binding.toString(binding) + | ||
| 672 | ", confidence number = " + String(Binding.actualConfidence(binding)) + ")\n"; | ||
| 673 | end for; | ||
| 674 | 1 | Error.addCompilerError(str); | |
| 675 | else | ||
| 676 | ✗ | Error.addMessage(Error.CONFLICTING_ALIAS_SET, {}); | |
| 677 | end if; | ||
| 678 | 1 | fail(); | |
| 679 | end if; | ||
| 680 | end selectFixedStartValue; | ||
| 681 | |||
| 682 | function defineAlias | ||
| 683 | input output FlowAlias alias; | ||
| 684 | input Binding binding; | ||
| 685 | input Boolean negated; | ||
| 686 | input output list<Equation> equations; | ||
| 687 | protected | ||
| 688 | Variable var; | ||
| 689 | Expression var_exp, bind_exp; | ||
| 690 | Equation bind_eq; | ||
| 691 | Binding b; | ||
| 692 | algorithm | ||
| 693 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
|
14 | SOME(var) := alias.variable; |
| 694 | |||
| 695 | // Move the variable's binding to an equation if it has one. | ||
| 696 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
|
14 | if Binding.isBound(var.binding) then |
| 697 | ✗ | var_exp := Expression.fromTypedCref(var.name, var.ty); | |
| 698 | ✗ | bind_exp := Binding.getExp(var.binding); | |
| 699 | ✗ | bind_eq := Equation.makeEquality(var_exp, bind_exp, var.ty); | |
| 700 | equations := bind_eq :: equations; | ||
| 701 | end if; | ||
| 702 | |||
| 703 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
|
14 | if negated then |
| 704 | 11 | b := Binding.mapExpShallow(binding, Expression.negate); | |
| 705 | else | ||
| 706 | b := binding; | ||
| 707 | end if; | ||
| 708 | |||
| 709 | // Replace the variable's binding with the given representative binding and | ||
| 710 | // mark it as an alias with a comment. | ||
| 711 | 14 | var.binding := b; | |
| 712 | var.comment := SCode.Comment.COMMENT(var.comment.annotation_, SOME("Alias variable")); | ||
| 713 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | alias.variable := SOME(var); |
| 714 | end defineAlias; | ||
| 715 | |||
| 716 | function evalAliasAttributes | ||
| 717 | "Evaluates the start, nominal, min, and max attributes for an alias and | ||
| 718 | appends them to the given lists if they're present. | ||
| 719 | |||
| 720 | Start/nominal values are returned as bindings since we need to know where | ||
| 721 | they come from, while for min/max we only need the values." | ||
| 722 | input output FlowAlias alias; | ||
| 723 | input Boolean negated; | ||
| 724 | input output list<tuple<ComponentRef, Binding>> startValues; | ||
| 725 | input output list<tuple<ComponentRef, Binding>> fixedStartValues; | ||
| 726 | input output list<tuple<ComponentRef, Binding>> nominalValues; | ||
| 727 | input output list<Expression> minValues; | ||
| 728 | input output list<Expression> maxValues; | ||
| 729 | protected | ||
| 730 | Variable var; | ||
| 731 | list<tuple<String, Binding>> attrs, accum_attrs = {}; | ||
| 732 | String attr_name; | ||
| 733 | Binding attr_binding; | ||
| 734 | Expression attr_exp; | ||
| 735 | algorithm | ||
| 736 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 27 times.
|
27 | SOME(var) := alias.variable; |
| 737 | 27 | attrs := var.typeAttributes; | |
| 738 | |||
| 739 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 27 times.
|
36 | for attr in attrs loop |
| 740 | 9 | (attr_name, attr_binding) := attr; | |
| 741 | |||
| 742 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | if Binding.hasExp(attr_binding) then |
| 743 | attr := match attr_name | ||
| 744 | case "start" | ||
| 745 | algorithm | ||
| 746 | 2 | attr_binding := evalAliasAttribute(attr_binding); | |
| 747 | |||
| 748 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
|
2 | if negated then |
| 749 | 1 | attr_binding := Binding.mapExpShallow(attr_binding, Expression.negate); | |
| 750 | end if; | ||
| 751 | |||
| 752 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | if Variable.isFixed(var) then |
| 753 | 2 | fixedStartValues := (var.name, attr_binding) :: fixedStartValues; | |
| 754 | else | ||
| 755 | ✗ | startValues := (var.name, attr_binding) :: startValues; | |
| 756 | end if; | ||
| 757 | 2 | then | |
| 758 | (attr_name, attr_binding); | ||
| 759 | |||
| 760 | case "nominal" | ||
| 761 | algorithm | ||
| 762 | ✗ | attr_binding := evalAliasAttribute(attr_binding); | |
| 763 | |||
| 764 | ✗ | if negated then | |
| 765 | ✗ | attr_binding := Binding.mapExpShallow(attr_binding, Expression.negate); | |
| 766 | end if; | ||
| 767 | |||
| 768 | ✗ | nominalValues := (var.name, attr_binding) :: nominalValues; | |
| 769 | ✗ | then | |
| 770 | (attr_name, attr_binding); | ||
| 771 | |||
| 772 | case "min" | ||
| 773 | algorithm | ||
| 774 | 4 | (attr_binding, attr_exp) := evalAliasAttribute(attr_binding); | |
| 775 | |||
| 776 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if negated then |
| 777 | ✗ | maxValues := Expression.negate(attr_exp) :: maxValues; | |
| 778 | else | ||
| 779 | 4 | minValues := attr_exp :: minValues; | |
| 780 | end if; | ||
| 781 | 4 | then | |
| 782 | (attr_name, attr_binding); | ||
| 783 | |||
| 784 | case "max" | ||
| 785 | algorithm | ||
| 786 | 1 | (attr_binding, attr_exp) := evalAliasAttribute(attr_binding); | |
| 787 | |||
| 788 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | if negated then |
| 789 | ✗ | minValues := Expression.negate(attr_exp) :: minValues; | |
| 790 | else | ||
| 791 | 1 | maxValues := attr_exp :: maxValues; | |
| 792 | end if; | ||
| 793 | 1 | then | |
| 794 | (attr_name, attr_binding); | ||
| 795 | |||
| 796 | else attr; | ||
| 797 | end match; | ||
| 798 | end if; | ||
| 799 | |||
| 800 | accum_attrs := attr :: accum_attrs; | ||
| 801 | end for; | ||
| 802 | |||
| 803 | 27 | accum_attrs := listReverseInPlace(accum_attrs); | |
| 804 | 27 | var.typeAttributes := accum_attrs; | |
| 805 |
1/2✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
|
27 | alias.variable := SOME(var); |
| 806 | end evalAliasAttributes; | ||
| 807 | |||
| 808 | function evalAliasAttribute | ||
| 809 | "Helper function to evalAliasAttributes, evaluates an attribute's binding." | ||
| 810 | input output Binding binding; | ||
| 811 | output Expression bindingExp; | ||
| 812 | algorithm | ||
| 813 | 7 | bindingExp := Binding.getExp(binding); | |
| 814 | 7 | Structural.markExp(bindingExp); | |
| 815 | 7 | bindingExp := Ceval.evalExp(bindingExp); | |
| 816 | 7 | binding := Binding.setExp(bindingExp, binding); | |
| 817 | end evalAliasAttribute; | ||
| 818 | |||
| 819 | function setRepresentativeAttributes | ||
| 820 | "Sets the start, nominal, min, and max attributes for an alias set representative." | ||
| 821 | input output FlowAlias alias; | ||
| 822 | input Binding startValue; | ||
| 823 | input Binding nominalValue; | ||
| 824 | input Binding minValue; | ||
| 825 | input Binding maxValue; | ||
| 826 | protected | ||
| 827 | Variable var; | ||
| 828 | list<tuple<String, Binding>> attrs = {}; | ||
| 829 | |||
| 830 | function add_attribute | ||
| 831 | input String name; | ||
| 832 | input Binding binding; | ||
| 833 | input output list<tuple<String, Binding>> attrs; | ||
| 834 | algorithm | ||
| 835 |
2/2✓ Branch 1 taken 39 times.
✓ Branch 2 taken 5 times.
|
44 | if Binding.isBound(binding) then |
| 836 | 5 | attrs := (name, binding) :: attrs; | |
| 837 | end if; | ||
| 838 | end add_attribute; | ||
| 839 | algorithm | ||
| 840 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
11 | SOME(var) := alias.variable; |
| 841 | |||
| 842 | // Remove any existing attributes. | ||
| 843 |
4/6✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 11 times.
|
16 | attrs := list(attr for attr guard not listMember(Util.tuple21(attr), |
| 844 | {"start", "nominal", "min", "max"}) in var.typeAttributes); | ||
| 845 | |||
| 846 | // Add the new attributes if they have a binding. | ||
| 847 | 11 | attrs := add_attribute("max", maxValue, attrs); | |
| 848 | 11 | attrs := add_attribute("min", minValue, attrs); | |
| 849 | 11 | attrs := add_attribute("nominal", nominalValue, attrs); | |
| 850 | 11 | attrs := add_attribute("start", startValue, attrs); | |
| 851 | |||
| 852 | 11 | var.typeAttributes := attrs; | |
| 853 | 11 | alias.variable := SOME(var); | |
| 854 | end setRepresentativeAttributes; | ||
| 855 | |||
| 856 | function findConstantBindings | ||
| 857 | input FlatModel flatModel; | ||
| 858 | input UnorderedMap<ComponentRef, Variable> vars; | ||
| 859 | algorithm | ||
| 860 |
2/2✓ Branch 0 taken 75 times.
✓ Branch 1 taken 7 times.
|
82 | for eq in flatModel.equations loop |
| 861 | 75 | findConstantBindingsInEq(eq, vars); | |
| 862 | end for; | ||
| 863 | end findConstantBindings; | ||
| 864 | |||
| 865 | function findConstantBindingsInEq | ||
| 866 | input Equation eq; | ||
| 867 | input UnorderedMap<ComponentRef, Variable> vars; | ||
| 868 | protected | ||
| 869 | ComponentRef cref; | ||
| 870 | Expression exp; | ||
| 871 | |||
| 872 | function update_binding | ||
| 873 | input Option<Variable> var; | ||
| 874 | input Expression value; | ||
| 875 | output Variable outVar; | ||
| 876 | algorithm | ||
| 877 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
|
5 | SOME(outVar) := var; |
| 878 | |||
| 879 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
|
5 | if Variable.isFlow(outVar) then |
| 880 | 5 | outVar.binding := Binding.makeFlat(value, Variability.CONSTANT, NFBinding.Source.GENERATED); | |
| 881 | end if; | ||
| 882 | end update_binding; | ||
| 883 | algorithm | ||
| 884 | () := match eq | ||
| 885 | case Equation.EQUALITY(lhs = Expression.CREF(cref = cref), rhs = exp) | ||
| 886 | guard ComponentRef.isFlow(cref) and Expression.variability(exp) <= Variability.STRUCTURAL_PARAMETER | ||
| 887 | algorithm | ||
| 888 | 5 | Structural.markExp(exp); | |
| 889 | 5 | exp := Ceval.tryEvalExp(exp); | |
| 890 | 5 | UnorderedMap.tryAddUpdate(cref, function update_binding(value = exp), vars); | |
| 891 | then | ||
| 892 | (); | ||
| 893 | |||
| 894 | case Equation.EQUALITY(lhs = exp, rhs = Expression.CREF(cref = cref)) | ||
| 895 | guard ComponentRef.isFlow(cref) and Expression.variability(exp) <= Variability.STRUCTURAL_PARAMETER | ||
| 896 | algorithm | ||
| 897 | ✗ | Structural.markExp(exp); | |
| 898 | ✗ | exp := Ceval.tryEvalExp(exp); | |
| 899 | ✗ | UnorderedMap.tryAddUpdate(cref, function update_binding(value = exp), vars); | |
| 900 | then | ||
| 901 | (); | ||
| 902 | |||
| 903 | else (); | ||
| 904 | end match; | ||
| 905 | end findConstantBindingsInEq; | ||
| 906 | |||
| 907 | annotation(__OpenModelica_Interface="nf_frontend"); | ||
| 908 | end NFStreamFlowAlias; | ||
| 909 |