Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 81.9% 177 / 0 / 216
Functions: -% 0 / 1 / 1
Branches: 64.0% 114 / 0 / 178

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