Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 93.8% 272 / 0 / 290
Functions: -% 0 / 1 / 1
Branches: 77.0% 174 / 0 / 226

OMCompiler/Compiler/NFFrontEnd/NFConnectEquations.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 NFConnectEquations
37 " file: ConnectEquations.mo
38 package: ConnectEquations
39 description: Functions that generate connect equations.
40
41 "
42
43 public
44 import CardinalityTable = NFCardinalityTable;
45 import ConnectionSets = NFConnectionSets.ConnectionSets;
46 import Connector = NFConnector;
47 import DAE;
48 import Equation = NFEquation;
49 import StreamFlowAlias = NFStreamFlowAlias;
50 import Variable = NFVariable;
51
52 protected
53 import ComponentRef = NFComponentRef;
54 import Config;
55 import ElementSource;
56 import Expression = NFExpression;
57 import Face = NFConnector.Face;
58 import List;
59 import NFPrefixes.{Variability, Purity, ConnectorType};
60 import Operator = NFOperator;
61 import Type = NFType;
62 import Call = NFCall;
63 import NFBuiltinFuncs;
64 import NFInstNode.InstNode;
65 import NFInstNode;
66 import Class = NFClass;
67 import Binding = NFBinding;
68 import NFFunction.Function;
69 import Global;
70 import BuiltinCall = NFBuiltinCall;
71 import ComplexType = NFComplexType;
72 import ExpandExp = NFExpandExp;
73 import Prefixes = NFPrefixes;
74 import Component = NFComponent;
75 import Ceval = NFCeval;
76 import MetaModelica.Dangerous.listReverseInPlace;
77 import SimplifyExp = NFSimplifyExp;
78 import UnorderedMap;
79 import Flatten = NFFlatten;
80 import Subscript = NFSubscript;
81 import Structural = NFStructural;
82
83 constant Expression EQ_ASSERT_STR =
84 Expression.STRING("Connected constants/parameters must be equal");
85
86 public
87 function generateEquations
88 input array<list<Connector>> sets;
89 input UnorderedMap<ComponentRef, Variable> variables;
90 output list<Equation> equations = {};
91 output UnorderedSet<ComponentRef> connectedLocalIOs;
92 output list<list<Connector>> unhandledStreamSets = {};
93 protected
94 partial function potFunc
95 input list<Connector> elements;
96 output list<Equation> equations;
97 input output UnorderedSet<ComponentRef> connectedLocalIOs;
98 end potFunc;
99
100 list<Equation> set_eql;
101 potFunc potfunc;
102 Expression flowThreshold;
103 ConnectorType.Type cty;
104 Boolean flow_alias_elim = Flags.isSet(Flags.FLOW_ALIAS_ELIMINATION);
105 algorithm
106 1571 setGlobalRoot(Global.isInStream, NONE());
107 1571 connectedLocalIOs := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
108 //potfunc := if Config.orderConnections() then
109 // generatePotentialEquationsOrdered else generatePotentialEquations;
110 potfunc := generatePotentialEquations;
111 1571 flowThreshold := Expression.REAL(Flags.getConfigReal(Flags.FLOW_THRESHOLD));
112
113
2/2
✓ Branch 1 taken 51242 times.
✓ Branch 2 taken 1571 times.
52813 for set in sets loop
114 51242 cty := getSetType(set);
115
116
2/2
✓ Branch 1 taken 33731 times.
✓ Branch 2 taken 17511 times.
51242 if ConnectorType.isPotential(cty) then
117 33731 (set_eql, connectedLocalIOs) := potfunc(set, connectedLocalIOs);
118 elseif ConnectorType.isFlow(cty) then
119 17269 set_eql := generateFlowEquations(set);
120 elseif ConnectorType.isStream(cty) then
121
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 231 times.
242 if flow_alias_elim then
122 // If doing flow alias elimination in stream connectors we need to
123 // save the stream connectors and process them later.
124 unhandledStreamSets := set :: unhandledStreamSets;
125 set_eql := {};
126 else
127 231 set_eql := generateStreamEquations(set, flowThreshold, variables, NONE());
128 end if;
129 else
130 ✗ Error.addInternalError(getInstanceName() + " got connection set with invalid type '" +
131 ConnectorType.toDebugString(cty) + "': " +
132 List.toString(set, Connector.toString, List.Style.FLAT_CURLY), sourceInfo());
133 ✗ fail();
134 end if;
135
136 51242 equations := listAppend(set_eql, equations);
137 end for;
138
139 1571 unhandledStreamSets := listReverseInPlace(unhandledStreamSets);
140 end generateEquations;
141
142 function generateStreamEquationsList
143 "Generates equations for a list of stream sets, for handling the stream sets
144 returned by generateEquations when flow alias elimination is used."
145 input list<list<Connector>> sets;
146 input UnorderedMap<ComponentRef, Variable> variables;
147 input StreamFlowAlias.Replacements replacements;
148 output list<Equation> equations = {};
149 protected
150 list<Equation> set_eql;
151 Expression flow_threshold;
152 algorithm
153 7 flow_threshold := Expression.REAL(Flags.getConfigReal(Flags.FLOW_THRESHOLD));
154
155
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 7 times.
17 for set in sets loop
156 10 set_eql := generateStreamEquations(set, flow_threshold, variables, SOME(replacements));
157 10 equations := listAppend(set_eql, equations);
158 end for;
159 end generateStreamEquationsList;
160
161 function evaluateOperators
162 input Expression exp;
163 input ConnectionSets.Sets sets;
164 input array<list<Connector>> setsArray;
165 input UnorderedMap<ComponentRef, Variable> variables;
166 input CardinalityTable.Table ctable;
167 input Option<StreamFlowAlias.Replacements> replacements;
168 output Expression evalExp;
169
170 import NFOperator.Op;
171 algorithm
172 evalExp := match exp
173 local
174 Call call;
175
176 case Expression.CALL(call = call)
177 then match call
178 case Call.TYPED_CALL()
179 then match Function.name(call.fn)
180 case Absyn.IDENT("inStream")
181 577 then evaluateInStream(Expression.toCref(listHead(call.arguments)), sets, setsArray, variables, ctable, replacements);
182 case Absyn.IDENT("actualStream")
183 algorithm
184 24 (evalExp, _) :=
185 evaluateActualStream(Expression.toCref(listHead(call.arguments)), sets, setsArray, variables, ctable, replacements);
186 then
187 evalExp;
188 case Absyn.IDENT("cardinality")
189 2376 then CardinalityTable.evaluateCardinality(listHead(call.arguments), ctable);
190 41031 else evaluateOperatorsShallow(exp, sets, setsArray, variables, ctable, replacements);
191 end match;
192
193 // inStream/actualStream can't handle non-literal subscripts, so reductions and array
194 // constructors containing such calls needs to be expanded to get rid of the iterators.
195 case Call.TYPED_REDUCTION()
196 guard Expression.contains(call.exp, isStreamCall)
197 1 then evaluateOperatorReductionExp(exp, sets, setsArray, variables, ctable, replacements);
198
199 case Call.TYPED_ARRAY_CONSTRUCTOR()
200 guard Expression.contains(call.exp, isStreamCall)
201 33 then evaluateOperatorArrayConstructorExp(exp, sets, setsArray, variables, ctable, replacements);
202
203 1443 else evaluateOperatorsShallow(exp, sets, setsArray, variables, ctable, replacements);
204 end match;
205
206 case Expression.BINARY(exp1 = Expression.CREF(),
207 operator = Operator.OPERATOR(op = Op.MUL),
208 exp2 = Expression.CALL(call = call as Call.TYPED_CALL()))
209 guard AbsynUtil.isNamedPathIdent(Function.name(call.fn), "actualStream")
210 38 then evaluateActualStreamMul(exp.exp1, listHead(call.arguments), exp.operator, sets, setsArray, variables, ctable, replacements);
211
212 case Expression.BINARY(exp1 = Expression.CALL(call = call as Call.TYPED_CALL()),
213 operator = Operator.OPERATOR(op = Op.MUL),
214 exp2 = Expression.CREF())
215 guard AbsynUtil.isNamedPathIdent(Function.name(call.fn), "actualStream")
216 ✗ then evaluateActualStreamMul(exp.exp2, listHead(call.arguments), exp.operator, sets, setsArray, variables, ctable, replacements);
217
218 // Only evaluate the branch that is taken if the condition is known, e.g. in an
219 // expanded array constructor {if i <= n then inStream(c[i].h) else ... for i in 1:n+1}
220 // the other branch may refer to connectors that don't exist.
221 case Expression.IF()
222 guard Expression.variability(exp.condition) <= Variability.STRUCTURAL_PARAMETER
223 algorithm
224 // The condition might still contain iterators, e.g. in a for-equation
225 // that hasn't been unrolled yet. Keep both branches then.
226 4416 evalExp := Ceval.tryEvalExp(exp.condition);
227
4/4
✓ Branch 1 taken 1637 times.
✓ Branch 2 taken 2779 times.
✓ Branch 4 taken 2715 times.
✓ Branch 5 taken 64 times.
4416 then
228 if Expression.isTrue(evalExp) then
229 evaluateOperators(exp.trueBranch, sets, setsArray, variables, ctable, replacements)
230 elseif Expression.isFalse(evalExp) then
231 evaluateOperators(exp.falseBranch, sets, setsArray, variables, ctable, replacements)
232 else
233 evaluateOperatorsShallow(exp, sets, setsArray, variables, ctable, replacements);
234
235 740539 else evaluateOperatorsShallow(exp, sets, setsArray, variables, ctable, replacements);
236 end match;
237 end evaluateOperators;
238
239 protected
240 function evaluateOperatorsShallow
241 input Expression exp;
242 input ConnectionSets.Sets sets;
243 input array<list<Connector>> setsArray;
244 input UnorderedMap<ComponentRef, Variable> variables;
245 input CardinalityTable.Table ctable;
246 input Option<StreamFlowAlias.Replacements> replacements;
247 output Expression evalExp;
248 algorithm
249 783077 evalExp := Expression.mapShallow(exp,
250 function evaluateOperators(sets = sets, setsArray = setsArray, variables = variables,
251 ctable = ctable, replacements = replacements));
252 end evaluateOperatorsShallow;
253
254 function getSetType
255 input list<Connector> set;
256 output ConnectorType.Type cty;
257 algorithm
258 // All connectors in a set should have the same type, so pick the first.
259
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 51242 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 51242 times.
51242 Connector.CONNECTOR(cty = cty) :: _ := set;
260 end getSetType;
261
262 function generatePotentialEquations
263 "Generating the equations for a set of potential variables means equating all
264 the components. For n components, this will give n-1 equations. For example,
265 if the set contains the components X, Y.A and Z.B, the equations generated
266 will be X = Y.A and X = Z.B."
267 input list<Connector> elements;
268 output list<Equation> equations;
269 input output UnorderedSet<ComponentRef> connectedLocalIOs;
270 protected
271 Connector c1;
272 algorithm
273 33731 c1 := listHead(elements);
274
275
2/2
✓ Branch 1 taken 33111 times.
✓ Branch 2 taken 620 times.
33731 if Connector.variability(c1) > Variability.PARAMETER then
276
4/4
✓ Branch 1 taken 44418 times.
✓ Branch 2 taken 33111 times.
✓ Branch 3 taken 44418 times.
✓ Branch 4 taken 33111 times.
77529 equations := list(makeEqualityEquation(c1.name, c1.source, c2.name, c2.source)
277 for c2 in listRest(elements));
278 // collect inputs and outputs that are inside in connections if --exposeLocalIOs > 0
279 // strip array indices so that the variables will be found later
280
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 33103 times.
33111 if Flags.getConfigInt(Flags.EXPOSE_LOCAL_IOS) > 0 then
281
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 8 times.
25 for c in elements loop
282
6/6
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 6 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 4 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 2 times.
17 if (Connector.isInside(c) and (ComponentRef.isInput(c.name) or ComponentRef.isOutput(c.name))) then
283 9 UnorderedSet.add(ComponentRef.stripSubscripts(c.name), connectedLocalIOs);
284 end if;
285 end for;
286 end if;
287 else
288 // don't create asserts for empty arrays
289
1/2
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
620 if Type.isEmptyArray(c1.ty) then
290 equations := {};
291 else
292
4/4
✓ Branch 1 taken 724 times.
✓ Branch 2 taken 620 times.
✓ Branch 3 taken 724 times.
✓ Branch 4 taken 620 times.
1344 equations := list(makeEqualityAssert(c1.name, c1.source, c2.name, c2.source)
293 for c2 in listRest(elements));
294 end if;
295 end if;
296 end generatePotentialEquations;
297
298 //function generatePotentialEquationsOrdered
299 // "Like generatePotentialEquations, but orders the connectors with
300 // shouldFlipPotentialEquation."
301 // input list<Connector> elements;
302 // output list<Equation> equations = {};
303 //protected
304 // partial function eqFunc
305 // input ComponentRef lhsCref;
306 // input DAE.ElementSource lhsSource;
307 // input ComponentRef rhsCref;
308 // input DAE.ElementSource rhsSource;
309 // output Equation eq;
310 // end eqFunc;
311 //
312 // Connector c1;
313 // ComponentRef cr1, cr2;
314 // DAE.ElementSource source;
315 // eqFunc eqfunc;
316 //algorithm
317 // if listEmpty(elements) then
318 // return;
319 // end if;
320 //
321 // c1 := listHead(elements);
322 // eqfunc := if Connector.variability(c1) > Variability.PARAMETER then
323 // makeEqualityEquation else makeEqualityAssert;
324 //
325 // cr1 := c1.name;
326 //
327 // for c2 in listRest(elements) loop
328 // cr2 := c2.name;
329 // (cr1, cr2) := Util.swap(shouldFlipPotentialEquation(cr1, c1.source), cr1, cr2);
330 // equations := eqfunc(cr1, c2.source, cr2, c2.source) :: equations;
331 // c1 := c2;
332 // cr1 := cr2;
333 // end for;
334 //end generatePotentialEquationsOrdered;
335
336 function makeEqualityEquation
337 input ComponentRef lhsCref;
338 input DAE.ElementSource lhsSource;
339 input ComponentRef rhsCref;
340 input DAE.ElementSource rhsSource;
341 output Equation equalityEq;
342 protected
343 DAE.ElementSource source;
344 algorithm
345 44418 source := ElementSource.mergeSources(lhsSource, rhsSource);
346 //source := ElementSource.addElementSourceConnect(source, (lhsCref, rhsCref));
347 44418 equalityEq := Equation.makeCrefEquality(lhsCref, rhsCref, InstNode.EMPTY_NODE(), source);
348 end makeEqualityEquation;
349
350 function makeEqualityAssert
351 input ComponentRef lhsCref;
352 input DAE.ElementSource lhsSource;
353 input ComponentRef rhsCref;
354 input DAE.ElementSource rhsSource;
355 output Equation equalityAssert;
356 protected
357 DAE.ElementSource source;
358 Expression lhs_exp, rhs_exp, exp;
359 Type ty, elem_ty;
360 list<InstNode> iterators = {};
361 list<Expression> ranges = {};
362 list<Subscript> subs;
363 algorithm
364 724 source := ElementSource.mergeSources(lhsSource, rhsSource);
365 //source := ElementSource.addElementSourceConnect(source, (lhsCref, rhsCref));
366
367 724 ty := ComponentRef.getSubscriptedType(lhsCref);
368
369 // if the type is array, get subscripts and add them to the cref
370 // use the iterators and ranges later to generate the for loop
371
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 724 times.
724 if Type.isArray(ty) then
372 ✗ (iterators, ranges, subs) := Flatten.makeIterators(lhsCref, Type.arrayDims(ty));
373 ✗ subs := listReverseInPlace(subs);
374 ✗ lhs_exp := Expression.fromCref(ComponentRef.mergeSubscripts(subs, lhsCref));
375 ✗ rhs_exp := Expression.fromCref(ComponentRef.mergeSubscripts(subs, rhsCref));
376 else
377 724 lhs_exp := Expression.fromCref(lhsCref);
378 724 rhs_exp := Expression.fromCref(rhsCref);
379 end if;
380
381 724 elem_ty := Type.arrayElementType(ty);
382
2/2
✓ Branch 1 taken 103 times.
✓ Branch 2 taken 621 times.
724 if Type.isReal(elem_ty) then
383 // Modelica doesn't allow == for Reals, so to keep the flat Modelica
384 // somewhat valid we use 'abs(lhs - rhs) <= 0' instead.
385 103 exp := Expression.BINARY(lhs_exp, Operator.makeSub(elem_ty), rhs_exp);
386 206 exp := Expression.CALL(Call.makeTypedCall(NFBuiltinFuncs.ABS_REAL, {exp}, Expression.variability(exp), Purity.PURE));
387 103 exp := Expression.RELATION(exp, Operator.makeLessEq(elem_ty), Expression.REAL(0.0), -1);
388 else
389 // For any other type, generate assertion for 'lhs == rhs'.
390 621 exp := Expression.RELATION(lhs_exp, Operator.makeEqual(elem_ty), rhs_exp, -1);
391 end if;
392
393 724 equalityAssert := Equation.ASSERT(exp, EQ_ASSERT_STR, NFBuiltin.ASSERTIONLEVEL_ERROR, NFInstNode.NO_SCOPE, source);
394
395 // wrap the equation in for loop if necessary
396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 724 times.
724 while not listEmpty(iterators) loop
397 ✗ equalityAssert := Equation.FOR(listHead(iterators), SOME(listHead(ranges)), {equalityAssert}, NFInstNode.NO_SCOPE, source);
398 ✗ iterators := listRest(iterators);
399 ✗ ranges := listRest(ranges);
400 end while;
401 end makeEqualityAssert;
402
403 //protected function shouldFlipPotentialEquation
404 // "If the flag +orderConnections=false is used, then we should keep the order of
405 // the connector elements as they occur in the connection (if possible). In that
406 // case we check if the cref of the first argument to the first connection
407 // stored in the element source is a prefix of the connector element cref. If
408 // it isn't, indicate that we should flip the generated equation."
409 // input DAE.ComponentRef lhsCref;
410 // input DAE.ElementSource lhsSource;
411 // output Boolean shouldFlip;
412 //algorithm
413 // shouldFlip := match lhsSource
414 // local
415 // DAE.ComponentRef lhs;
416 //
417 // case DAE.SOURCE(connectEquationOptLst = (lhs, _) :: _)
418 // then not ComponentReferenceBasics.crefPrefixOf(lhs, lhsCref);
419 //
420 // else false;
421 // end match;
422 //end shouldFlipPotentialEquation;
423
424 function generateFlowEquations
425 input list<Connector> elements;
426 output list<Equation> equations;
427 protected
428 Connector c;
429 list<Connector> c_rest;
430 DAE.ElementSource src;
431 Expression sum;
432 list<Expression> terms;
433 list<InstNode> iterators = {};
434 list<Expression> ranges = {};
435 list<Subscript> subs = {};
436 algorithm
437
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17269 times.
17269 c :: c_rest := elements;
438 17269 src := c.source;
439
440
2/2
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 17233 times.
17269 if Connector.isArray(c) then
441 36 (iterators, ranges, subs) := Flatten.makeIterators(c.name, Type.arrayDims(c.ty));
442 36 subs := listReverseInPlace(subs);
443
5/6
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 62 times.
✓ Branch 3 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 36 times.
98 c :: c_rest := list(Connector.addSubscripts(subs, e) for e in elements);
444 end if;
445
446
2/2
✓ Branch 0 taken 1850 times.
✓ Branch 1 taken 15419 times.
17269 if listEmpty(c_rest) then
447 1850 sum := Expression.fromCref(c.name);
448 else
449 15419 terms := {makeFlowExp(c)};
450
451
2/2
✓ Branch 0 taken 21352 times.
✓ Branch 1 taken 15419 times.
36771 for e in c_rest loop
452 21352 terms := makeFlowExp(e)::terms;
453 21352 src := ElementSource.mergeSources(src, e.source);
454 end for;
455
456 15419 sum := Expression.MULTARY(listReverseInPlace(terms), {}, Operator.makeAdd(Type.REAL()));
457 end if;
458
459 17269 equations := {Equation.makeEquality(sum, Expression.REAL(0.0), Type.arrayElementType(c.ty), src)};
460
461
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 17269 times.
17311 while not listEmpty(iterators) loop
462 84 equations := {Equation.FOR(listHead(iterators), SOME(listHead(ranges)), equations, NFInstNode.NO_SCOPE, src)};
463 42 iterators := listRest(iterators);
464 42 ranges := listRest(ranges);
465 end while;
466 end generateFlowEquations;
467
468 function makeFlowExp
469 "Creates an expression from a connector element, which is the element itself
470 if it's an inside connector, or the element negated if it's outside."
471 input Connector element;
472 output Expression exp;
473 protected
474 Face face;
475 algorithm
476 36771 exp := Expression.fromCref(element.name);
477
478 // TODO: Remove unnecessary variable 'face' once #4502 is fixed.
479 36771 face := element.face;
480
2/2
✓ Branch 0 taken 27658 times.
✓ Branch 1 taken 9113 times.
36771 if face == Face.OUTSIDE then
481 9113 exp := Expression.UNARY(Operator.makeUMinus(Type.REAL()), exp);
482 end if;
483 end makeFlowExp;
484
485 function generateStreamEquations
486 "Generates the equations for a stream connection set."
487 input list<Connector> elements;
488 input Expression flowThreshold;
489 input UnorderedMap<ComponentRef, Variable> variables;
490 input Option<StreamFlowAlias.Replacements> replacements;
491 output list<Equation> equations;
492 protected
493 ComponentRef cr1, cr2;
494 DAE.ElementSource src, src1, src2;
495 Expression cref1, cref2, e1, e2;
496 list<Connector> inside, outside;
497 algorithm
498 241 (outside, inside) := List.splitOnTrue(elements, Connector.isOutside);
499
6/6
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 450 times.
✓ Branch 3 taken 512 times.
✓ Branch 4 taken 241 times.
✓ Branch 5 taken 450 times.
✓ Branch 6 taken 241 times.
753 inside := list(s for s guard not isNoFlowInside(s, variables, NONE()) in inside);
500
501 equations := match (inside, outside)
502 // Unconnected stream connector, do nothing.
503 case ({_}, {}) then {};
504
505 // Both inside, do nothing.
506 case ({_, _}, {}) then {};
507
508 // Both outside:
509 // cr1 = inStream(cr2);
510 // cr2 = inStream(cr1);
511 case ({}, {Connector.CONNECTOR(name = cr1, source = src1),
512 Connector.CONNECTOR(name = cr2, source = src2)})
513 algorithm
514 3 cref1 := Expression.fromCref(cr1);
515 3 cref2 := Expression.fromCref(cr2);
516 6 e1 := makeInStreamCall(cref2);
517 6 e2 := makeInStreamCall(cref1);
518 3 src := ElementSource.mergeSources(src1, src2);
519 6 then
520 {Equation.makeEquality(cref1, e1, Type.REAL(), src),
521 Equation.makeEquality(cref2, e2, Type.REAL(), src)};
522
523 // One inside, one outside:
524 // cr1 = cr2;
525 case ({Connector.CONNECTOR(name = cr1, source = src1)},
526 {Connector.CONNECTOR(name = cr2, source = src2)})
527 algorithm
528 17 src := ElementSource.mergeSources(src1, src2);
529 17 then
530 {Equation.makeCrefEquality(cr1, cr2, InstNode.EMPTY_NODE(), src)};
531
532 // The general case with N inside connectors and M outside:
533 41 else streamEquationGeneral(outside, inside, flowThreshold, variables, replacements);
534
535 end match;
536 end generateStreamEquations;
537
538 function streamEquationGeneral
539 "Generates an equation for an outside stream connector element."
540 input list<Connector> outsideElements;
541 input list<Connector> insideElements;
542 input Expression flowThreshold;
543 input UnorderedMap<ComponentRef, Variable> variables;
544 input Option<StreamFlowAlias.Replacements> replacements;
545 output list<Equation> equations = {};
546 protected
547 list<Connector> reduced_outside, outside;
548 Expression cref_exp, res;
549 DAE.ElementSource src;
550 algorithm
551
5/6
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 41 times.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 41 times.
53 reduced_outside := list(s for s guard not isNoFlowOutside(s, variables, NONE()) in outsideElements);
552
553
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 41 times.
53 for e in outsideElements loop
554 12 cref_exp := Expression.fromCref(e.name);
555 12 outside := removeStreamSetElement(e.name, reduced_outside);
556 12 res := streamSumEquationExp(outside, insideElements, flowThreshold, Expression.INTEGER(0), variables, replacements);
557 12 src := ElementSource.addAdditionalComment(e.source, " equation generated from stream connection");
558 12 equations := Equation.makeEquality(cref_exp, res, Type.REAL(), src) :: equations;
559 end for;
560 end streamEquationGeneral;
561
562 function streamSumEquationExp
563 "Generates the sum expression used by stream connector equations, given M
564 outside connectors and N inside connectors:
565
566 (sum(max(-flow_exp[i], eps) * stream_exp[i] for i in N) +
567 sum(max( flow_exp[i], eps) * inStream(stream_exp[i]) for i in M)) /
568 (sum(max(-flow_exp[i], eps) for i in N) +
569 sum(max( flow_exp[i], eps) for i in M))
570
571 where eps = inFlowThreshold.
572 "
573 input list<Connector> outsideElements;
574 input list<Connector> insideElements;
575 input Expression flowThreshold;
576 input Expression fallback;
577 input UnorderedMap<ComponentRef, Variable> variables;
578 input Option<StreamFlowAlias.Replacements> replacements;
579 output Expression sumExp;
580 protected
581 Expression outside_sum1, outside_sum2, inside_sum1, inside_sum2;
582 list<Boolean> outside_non_negative, inside_non_negative;
583 algorithm
584 207 (outside_non_negative, inside_non_negative) := setRequiresPositiveMax(outsideElements, insideElements, variables, replacements);
585
586 sumExp := match (listEmpty(outsideElements), listEmpty(insideElements))
587 case (true, true) then fallback;
588
589 case (true, false)
590 algorithm
591 // No outside components.
592 195 inside_sum1 := sumMap(insideElements, sumInside1, flowThreshold, inside_non_negative, variables);
593 195 inside_sum2 := sumMap(insideElements, sumInside2, flowThreshold, inside_non_negative, variables);
594 195 sumExp := Expression.BINARY(inside_sum1, Operator.makeDiv(Type.REAL()), inside_sum2);
595 195 then makeInStreamDivCall(sumExp, fallback);
596
597 case (false, true)
598 algorithm
599 // No inside components.
600 6 outside_sum1 := sumMap(outsideElements, sumOutside1, flowThreshold, outside_non_negative, variables);
601 6 outside_sum2 := sumMap(outsideElements, sumOutside2, flowThreshold, outside_non_negative, variables);
602 6 sumExp := Expression.BINARY(outside_sum1, Operator.makeDiv(Type.REAL()), outside_sum2);
603 6 then makeInStreamDivCall(sumExp, fallback);
604
605 case (false, false)
606 algorithm
607 // Both outside and inside components.
608 3 outside_sum1 := sumMap(outsideElements, sumOutside1, flowThreshold, outside_non_negative, variables);
609 3 outside_sum2 := sumMap(outsideElements, sumOutside2, flowThreshold, outside_non_negative, variables);
610 3 inside_sum1 := sumMap(insideElements, sumInside1, flowThreshold, inside_non_negative, variables);
611 3 inside_sum2 := sumMap(insideElements, sumInside2, flowThreshold, inside_non_negative, variables);
612 3 sumExp := Expression.BINARY(
613 Expression.BINARY(outside_sum1, Operator.makeAdd(Type.REAL()), inside_sum1),
614 Operator.makeDiv(Type.REAL()),
615 Expression.BINARY(outside_sum2, Operator.makeAdd(Type.REAL()), inside_sum2));
616 3 then makeInStreamDivCall(sumExp, fallback);
617 end match;
618 end streamSumEquationExp;
619
620 function setRequiresPositiveMax
621 "Returns whether the flow variable for each outside/inside connector needs to
622 be wrapped in positiveMax or not when generating stream connector equations.
623 An inside (outside) flow variable does not need to be wrapped in positiveMax
624 if it has max <= 0 (min >= 0) or is set to a constant value < 0 (> 0), and
625 at least one flow variable in the set has max < 0 (min > 0) or is set to a
626 constant value that is < 0 (> 0)."
627 input list<Connector> outsideElements;
628 input list<Connector> insideElements;
629 input UnorderedMap<ComponentRef, Variable> variables;
630 input Option<StreamFlowAlias.Replacements> replacements;
631 output list<Boolean> outsidePositiveMax = {};
632 output list<Boolean> insidePositiveMax = {};
633 protected
634 Boolean positive_flow, non_negative_flow, any_positive_flow = false;
635 algorithm
636
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 207 times.
222 for c in outsideElements loop
637 15 (positive_flow, non_negative_flow) := hasFlowOutside(c, variables, replacements);
638 15 any_positive_flow := any_positive_flow or positive_flow;
639
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 outsidePositiveMax := not non_negative_flow :: outsidePositiveMax;
640 end for;
641
642
2/2
✓ Branch 0 taken 440 times.
✓ Branch 1 taken 207 times.
647 for c in insideElements loop
643 440 (positive_flow, non_negative_flow) := hasFlowInside(c, variables, replacements);
644 440 any_positive_flow := any_positive_flow or positive_flow;
645
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 436 times.
440 insidePositiveMax := not non_negative_flow :: insidePositiveMax;
646 end for;
647
648
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 205 times.
207 if any_positive_flow then
649 // Return the lists if at least one connector has positive flow.
650 2 outsidePositiveMax := listReverseInPlace(outsidePositiveMax);
651 2 insidePositiveMax := listReverseInPlace(insidePositiveMax);
652 else
653 // Otherwise, every connector needs to be wrapped in positiveMax.
654
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 205 times.
220 outsidePositiveMax := list(true for c in outsideElements);
655
2/2
✓ Branch 0 taken 436 times.
✓ Branch 1 taken 205 times.
641 insidePositiveMax := list(true for c in insideElements);
656 end if;
657 end setRequiresPositiveMax;
658
659 function hasFlowOutside
660 input Connector conn;
661 input UnorderedMap<ComponentRef, Variable> variables;
662 input Option<StreamFlowAlias.Replacements> replacements;
663 output Boolean positiveFlow;
664 output Boolean nonNegativeFlow;
665 algorithm
666 15 (positiveFlow, nonNegativeFlow) := hasFlow(conn, true, variables, replacements);
667 end hasFlowOutside;
668
669 function hasFlowInside
670 input Connector conn;
671 input UnorderedMap<ComponentRef, Variable> variables;
672 input Option<StreamFlowAlias.Replacements> replacements;
673 output Boolean positiveFlow;
674 output Boolean nonNegativeFlow;
675 algorithm
676 440 (positiveFlow, nonNegativeFlow) := hasFlow(conn, true, variables, replacements);
677 end hasFlowInside;
678
679 function hasFlow
680 "Returns whether the flow variable of an inside (outside) connector has
681 positive and/or non-negative flow."
682 input Connector conn;
683 input Boolean isInside;
684 input UnorderedMap<ComponentRef, Variable> variables;
685 input Option<StreamFlowAlias.Replacements> replacements;
686 output Boolean positiveFlow = false;
687 output Boolean nonNegativeFlow = false;
688 protected
689 Option<Expression> oexp;
690 Expression exp;
691 Variability var;
692 Variable v;
693 Boolean is_inside = isInside, negated;
694 algorithm
695 455 (v, negated) := lookupFlowVarInConnector(conn, variables, replacements);
696
697
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 450 times.
455 if negated then
698 5 is_inside := not is_inside;
699 end if;
700
701
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 450 times.
460 oexp := lookupAttrInVar(if is_inside then "max" else "min", v);
702
703
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 455 times.
✓ Branch 2 taken 417 times.
✓ Branch 3 taken 38 times.
455 if isSome(oexp) then
704 417 exp := evaluateAttribute(Util.getOption(oexp));
705
706
2/2
✓ Branch 0 taken 414 times.
✓ Branch 1 taken 3 times.
417 if is_inside then
707 414 positiveFlow := Expression.isNegative(exp); // positive flow if max < 0
708 414 nonNegativeFlow := Expression.isNonPositive(exp); // non-negative flow if max <= 0
709 else
710 3 positiveFlow := Expression.isPositive(exp); // positive flow if min > 0
711 3 nonNegativeFlow := Expression.isNonNegative(exp); // non-negative flow if min >= 0
712 end if;
713 end if;
714
715 // Check the binding too if the min/max is inconclusive.
716
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 454 times.
455 if not positiveFlow then
717 454 oexp := Binding.getExpOpt(v.binding);
718 // Positive flow if binding < 0 if inside, > 0 if outside.
719
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 450 times.
458 positiveFlow := Util.applyOptionOrDefault(oexp,
720 if is_inside then Expression.isNegative else Expression.isPositive, false);
721 // Non-negative flow if either the binding or min/max indicates it.
722 454 nonNegativeFlow := nonNegativeFlow or positiveFlow;
723 end if;
724 end hasFlow;
725
726 function sumMap
727 "Creates a sum expression by applying the given function on the list of
728 elements and summing up the resulting expressions."
729 input list<Connector> elements;
730 input FuncType func;
731 input Expression flowThreshold;
732 input list<Boolean> needsPositiveMax;
733 input UnorderedMap<ComponentRef, Variable> variables;
734 output Expression exp;
735
736 partial function FuncType
737 input Connector element;
738 input Expression flowThreshold;
739 input Boolean needsPositiveMax;
740 input UnorderedMap<ComponentRef, Variable> variables;
741 output Expression exp;
742 end FuncType;
743 protected
744 Boolean needs_positive_max;
745 list<Boolean> needs_positive_max_rest;
746 algorithm
747
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 414 times.
414 needs_positive_max :: needs_positive_max_rest := needsPositiveMax;
748
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 412 times.
416 exp := func(listHead(elements), flowThreshold, needs_positive_max, variables);
749
2/2
✓ Branch 1 taken 496 times.
✓ Branch 2 taken 414 times.
910 for e in listRest(elements) loop
750
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 496 times.
496 needs_positive_max :: needs_positive_max_rest := needs_positive_max_rest;
751
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 496 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 492 times.
500 exp := Expression.BINARY(func(e, flowThreshold, needs_positive_max, variables), Operator.makeAdd(Type.REAL()), exp);
752 end for;
753 end sumMap;
754
755 function streamFlowExp
756 "Returns the stream and flow component in a stream set element as expressions."
757 input Connector element;
758 output Expression streamExp;
759 output Expression flowExp;
760 protected
761 ComponentRef stream_cr;
762 algorithm
763 455 stream_cr := Connector.name(element);
764 455 streamExp := Expression.fromCref(stream_cr);
765 455 flowExp := Expression.fromCref(associatedFlowCref(stream_cr));
766 end streamFlowExp;
767
768 function flowExp
769 "Returns the flow component in a stream set element as an expression."
770 input Connector element;
771 output Expression flowExp;
772 protected
773 ComponentRef flow_cr;
774 algorithm
775 2398 flow_cr := associatedFlowCref(Connector.name(element));
776 2398 flowExp := Expression.fromCref(flow_cr);
777 end flowExp;
778
779 function sumOutside1
780 "Helper function to streamSumEquationExp. Returns the expression
781 max(flow_exp, eps) * inStream(stream_exp)
782 given a stream set element."
783 input Connector element;
784 input Expression flowThreshold;
785 input Boolean needsPositiveMax;
786 input UnorderedMap<ComponentRef, Variable> variables;
787 output Expression exp;
788 protected
789 Expression stream_exp, flow_exp;
790 algorithm
791 15 (stream_exp, flow_exp) := streamFlowExp(element);
792
793
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if needsPositiveMax then
794 15 flow_exp := makePositiveMaxCall(flow_exp, element, flowThreshold, variables);
795 end if;
796
797 30 exp := Expression.BINARY(flow_exp, Operator.makeMul(Type.REAL()), makeInStreamCall(stream_exp));
798 end sumOutside1;
799
800 function sumInside1
801 "Helper function to streamSumEquationExp. Returns the expression
802 max(-flow_exp, eps) * stream_exp
803 given a stream set element."
804 input Connector element;
805 input Expression flowThreshold;
806 input Boolean needsPositiveMax;
807 input UnorderedMap<ComponentRef, Variable> variables;
808 output Expression exp;
809 protected
810 Expression stream_exp, flow_exp;
811 algorithm
812 440 (stream_exp, flow_exp) := streamFlowExp(element);
813 440 flow_exp := Expression.UNARY(Operator.makeUMinus(Type.REAL()), flow_exp);
814
815
2/2
✓ Branch 0 taken 437 times.
✓ Branch 1 taken 3 times.
440 if needsPositiveMax then
816 437 flow_exp := makePositiveMaxCall(flow_exp, element, flowThreshold, variables);
817 end if;
818
819 440 exp := Expression.BINARY(flow_exp, Operator.makeMul(Type.REAL()), stream_exp);
820 end sumInside1;
821
822 function sumOutside2
823 "Helper function to streamSumEquationExp. Returns the expression
824 max(flow_exp, eps)
825 given a stream set element."
826 input Connector element;
827 input Expression flowThreshold;
828 input Boolean needsPositiveMax;
829 input UnorderedMap<ComponentRef, Variable> variables;
830 output Expression exp;
831 algorithm
832 15 exp := flowExp(element);
833
834
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if needsPositiveMax then
835 15 exp := makePositiveMaxCall(exp, element, flowThreshold, variables);
836 end if;
837 end sumOutside2;
838
839 function sumInside2
840 "Helper function to streamSumEquationExp. Returns the expression
841 max(-flow_exp, eps)
842 given a stream set element."
843 input Connector element;
844 input Expression flowThreshold;
845 input Boolean needsPositiveMax;
846 input UnorderedMap<ComponentRef, Variable> variables;
847 output Expression exp;
848 algorithm
849 440 exp := flowExp(element);
850 440 exp := Expression.UNARY(Operator.makeUMinus(Type.REAL()), exp);
851
852
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 437 times.
440 if needsPositiveMax then
853 437 exp := makePositiveMaxCall(exp, element, flowThreshold, variables);
854 end if;
855 end sumInside2;
856
857 function makeInStreamCall
858 "Creates an inStream call expression."
859 input Expression streamExp;
860 output Expression inStreamCall;
861 annotation(__OpenModelica_EarlyInline = true);
862 algorithm
863 ✗ inStreamCall := Expression.CALL(Call.makeTypedCall(
864 NFBuiltinFuncs.IN_STREAM, {streamExp}, Expression.variability(streamExp), Purity.PURE));
865 end makeInStreamCall;
866
867 function makePositiveMaxCall
868 "Generates a max(flow_exp, eps) call."
869 input Expression flowExp;
870 input Connector element;
871 input Expression flowThreshold;
872 input UnorderedMap<ComponentRef, Variable> variables;
873 output Expression positiveMaxCall;
874 protected
875 ComponentRef flow_name;
876 Option<Expression> nominal_oexp;
877 Expression nominal_exp, flow_threshold;
878 InstNode fn_node;
879 Function fn;
880 algorithm
881 904 flow_name := associatedFlowCref(Connector.name(element));
882 904 nominal_oexp := lookupVarAttr(flow_name, "nominal", variables);
883
884
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 904 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 872 times.
904 if isSome(nominal_oexp) then
885 32 SOME(nominal_exp) := nominal_oexp;
886 32 flow_threshold := Expression.BINARY(flowThreshold, Operator.makeMul(Type.REAL()), nominal_exp);
887 else
888 flow_threshold := flowThreshold;
889 end if;
890
891
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 886 times.
904 if Flags.getConfigBool(Flags.BASE_MODELICA) then
892 18 fn_node := Class.lookupElement("$OMC$PositiveMax",
893 InstNode.getClass(InstNode.topScope(ComponentRef.node(flow_name))));
894 18 fn_node := Function.instFunctionNode(fn_node, NFInstContext.NO_CONTEXT, Absyn.dummyInfo);
895
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 {fn} := Function.typeNodeCache(fn_node);
896 18 positiveMaxCall := Expression.CALL(Call.makeTypedCall(fn,
897 {flowExp, flow_threshold}, Connector.variability(element), Purity.PURE));
898 else
899 1772 positiveMaxCall := Expression.CALL(Call.makeTypedCall(NFBuiltinFuncs.POSITIVE_MAX_REAL,
900 {flowExp, flow_threshold}, Connector.variability(element), Purity.PURE));
901 end if;
902
903 904 setGlobalRoot(Global.isInStream, SOME(true));
904 end makePositiveMaxCall;
905
906 function makeInStreamDivCall
907 "Generates the call `inStreamDiv(sum_exp, fallback)`. This is a wrapper around
908 `sum_exp = streamSumEquationExp`, which is removed in the backend once all
909 calls to `positiveMax` can be simplified. If they are all zero this call is
910 reduced to `fallback`, otherwise it is `sum_exp`."
911 input Expression sum_exp;
912 input Expression fallback;
913 output Expression inStreamDivCall;
914 algorithm
915
2/2
✓ Branch 1 taken 198 times.
✓ Branch 2 taken 6 times.
204 if Flags.getConfigBool(Flags.BASE_MODELICA) then
916 // TODO maybe do the same as for `positiveMax`?
917 inStreamDivCall := sum_exp;
918 else
919 396 inStreamDivCall := Expression.CALL(Call.makeTypedCall(NFBuiltinFuncs.INSTREAM_DIV_REAL,
920 {sum_exp, fallback}, Expression.variability(fallback), Purity.PURE));
921 end if;
922 end makeInStreamDivCall;
923
924 function isStreamCall
925 input Expression exp;
926 output Boolean streamCall;
927 algorithm
928 streamCall := match exp
929 local
930 String name;
931
932 case Expression.CALL()
933 then match Function.name(Call.typedFunction(exp.call))
934 case Absyn.IDENT("inStream") then true;
935 case Absyn.IDENT("actualStream") then true;
936 else false;
937 end match;
938
939 else false;
940 end match;
941 end isStreamCall;
942
943 function evaluateOperatorReductionExp
944 input Expression exp;
945 input ConnectionSets.Sets sets;
946 input array<list<Connector>> setsArray;
947 input UnorderedMap<ComponentRef, Variable> variables;
948 input CardinalityTable.Table ctable;
949 input Option<StreamFlowAlias.Replacements> replacements;
950 output Expression evalExp;
951 protected
952 Call call;
953 Function fn;
954 Type ty;
955 Expression arg, iter_exp;
956 list<tuple<InstNode, Expression>> iters = {};
957 InstNode iter_node;
958 algorithm
959 evalExp := match exp
960 case Expression.CALL(call = call as Call.TYPED_REDUCTION())
961 algorithm
962 1 ty := Expression.typeOf(call.exp);
963
964
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 for iter in call.iters loop
965 1 (iter_node, iter_exp) := iter;
966
967
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 if Component.variability(InstNode.component(iter_node)) > Variability.PARAMETER then
968 ✗ print("Iteration range in reduction containing connector operator calls must be a parameter expression.");
969 ✗ fail();
970 end if;
971
972 1 iter_exp := Ceval.evalExp(iter_exp);
973 1 ty := Type.liftArrayLeftList(ty, Type.arrayDims(Expression.typeOf(iter_exp)));
974 1 iters := (iter_node, iter_exp) :: iters;
975 end for;
976
977 1 iters := listReverseInPlace(iters);
978 1 arg := ExpandExp.expandArrayConstructor(call.exp, ty, iters);
979 1 then
980 Expression.CALL(Call.makeTypedCall(call.fn, {arg}, call.var, Purity.PURE, call.ty));
981
982 end match;
983
984 1 evalExp := evaluateOperators(evalExp, sets, setsArray, variables, ctable, replacements);
985 end evaluateOperatorReductionExp;
986
987 function evaluateOperatorArrayConstructorExp
988 input Expression exp;
989 input ConnectionSets.Sets sets;
990 input array<list<Connector>> setsArray;
991 input UnorderedMap<ComponentRef, Variable> variables;
992 input CardinalityTable.Table ctable;
993 input Option<StreamFlowAlias.Replacements> replacements;
994 output Expression evalExp;
995 protected
996 Boolean expanded;
997 algorithm
998 33 (evalExp, expanded) := ExpandExp.expand(exp);
999
1000
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 if not expanded then
1001 ✗ Error.addInternalError(getInstanceName() +
1002 " failed to expand call containing stream operator: " +
1003 Expression.toString(exp), sourceInfo());
1004 end if;
1005
1006 33 evalExp := evaluateOperators(evalExp, sets, setsArray, variables, ctable, replacements);
1007 end evaluateOperatorArrayConstructorExp;
1008
1009 function evaluateInStream
1010 "Evaluates the inStream operator with the given cref as argument."
1011 input ComponentRef cref;
1012 input ConnectionSets.Sets sets;
1013 input array<list<Connector>> setsArray;
1014 input UnorderedMap<ComponentRef, Variable> variables;
1015 input CardinalityTable.Table ctable;
1016 input Option<StreamFlowAlias.Replacements> replacements;
1017 output Expression exp;
1018 protected
1019 Connector c;
1020 list<Connector> sl;
1021 Integer set;
1022 ComponentRef cr;
1023 algorithm
1024 660 cr := ComponentRef.evaluateSubscripts(cref);
1025 660 c := Connector.CONNECTOR(cr, Type.UNKNOWN(), Face.INSIDE,
1026 ConnectorType.STREAM, DAE.emptyElementSource);
1027
1028 try
1029 660 set := ConnectionSets.findSetArrayIndex(c, sets);
1030 624 sl := arrayGet(setsArray, set);
1031 else
1032 sl := {c};
1033 end try;
1034
1035 660 exp := generateInStreamExp(cr, sl, sets, setsArray, variables, ctable, replacements,
1036 Flags.getConfigReal(Flags.FLOW_THRESHOLD));
1037
1038
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 660 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 647 times.
660 if isSome(replacements) then
1039 13 exp := StreamFlowAlias.applyReplacementsInExp(Util.getOption(replacements), exp);
1040 end if;
1041 end evaluateInStream;
1042
1043 function generateInStreamExp
1044 "Helper function to evaluateInStream. Generates an expression for inStream
1045 given a connection set."
1046 input ComponentRef streamCref;
1047 input list<Connector> streams;
1048 input ConnectionSets.Sets sets;
1049 input array<list<Connector>> setsArray;
1050 input UnorderedMap<ComponentRef, Variable> variables;
1051 input CardinalityTable.Table ctable;
1052 input Option<StreamFlowAlias.Replacements> replacements;
1053 input Real flowThreshold;
1054 output Expression exp;
1055 protected
1056 list<Connector> reducedStreams, inside, outside;
1057 ComponentRef cr;
1058 Face f1, f2;
1059 algorithm
1060
6/6
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 1508 times.
✓ Branch 3 taken 1624 times.
✓ Branch 4 taken 660 times.
✓ Branch 5 taken 1508 times.
✓ Branch 6 taken 660 times.
2284 reducedStreams := list(s for s guard not isNoFlowMinMax(s, streamCref, variables, replacements) in streams);
1061
1062 exp := match reducedStreams
1063 // Unconnected stream connector:
1064 // inStream(c) = c;
1065 case {Connector.CONNECTOR(face = Face.INSIDE)}
1066 54 then Expression.fromCref(streamCref);
1067
1068 // Two inside connected stream connectors:
1069 // inStream(c1) = c2;
1070 // inStream(c2) = c1;
1071 case {Connector.CONNECTOR(face = Face.INSIDE),
1072 Connector.CONNECTOR(face = Face.INSIDE)}
1073 algorithm
1074
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 390 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 390 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 390 times.
390 {Connector.CONNECTOR(name = cr)} :=
1075 removeStreamSetElement(streamCref, reducedStreams);
1076 390 then
1077 Expression.fromCref(cr);
1078
1079 // One inside, one outside connected stream connector:
1080 // inStream(c1) = inStream(c2);
1081 case {Connector.CONNECTOR(face = f1),
1082 Connector.CONNECTOR(face = f2)} guard f1 <> f2
1083 algorithm
1084
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 21 times.
21 {Connector.CONNECTOR(name = cr)} :=
1085 removeStreamSetElement(streamCref, reducedStreams);
1086 21 then
1087 evaluateInStream(cr, sets, setsArray, variables, ctable, replacements);
1088
1089 // The general case:
1090 else
1091 algorithm
1092 195 (outside, inside) := List.splitOnTrue(reducedStreams, Connector.isOutside);
1093 195 inside := removeStreamSetElement(streamCref, inside);
1094 195 exp := streamSumEquationExp(outside, inside, Expression.REAL(flowThreshold), Expression.fromCref(streamCref), variables, replacements);
1095 // Evaluate any inStream calls that were generated.
1096 195 exp := evaluateOperators(exp, sets, setsArray, variables, ctable, replacements);
1097 then
1098 exp;
1099
1100 end match;
1101 end generateInStreamExp;
1102
1103 function isNoFlowMinMax
1104 "Returns true if the given flow attribute of a connector is zero."
1105 input Connector conn;
1106 input ComponentRef streamCref;
1107 input UnorderedMap<ComponentRef, Variable> variables;
1108 input Option<StreamFlowAlias.Replacements> replacements;
1109 output Boolean noFlow;
1110 algorithm
1111
2/2
✓ Branch 1 taken 964 times.
✓ Branch 2 taken 660 times.
1624 if ComponentRef.isEqual(streamCref, conn.name) then
1112 noFlow := false;
1113 elseif Connector.isOutside(conn) then
1114 25 noFlow := isNoFlowOutside(conn, variables, replacements);
1115 else
1116 939 noFlow := isNoFlowInside(conn, variables, replacements);
1117 end if;
1118 end isNoFlowMinMax;
1119
1120 function isNoFlowOutside
1121 "Returns true if the given outside stream connector has no flow, which occurs
1122 when its max attribute <= 0."
1123 input Connector conn;
1124 input UnorderedMap<ComponentRef, Variable> variables;
1125 input Option<StreamFlowAlias.Replacements> replacements;
1126 output Boolean noFlow;
1127 algorithm
1128 37 noFlow := isNoFlow(conn, false, variables, replacements);
1129 end isNoFlowOutside;
1130
1131 function isNoFlowInside
1132 "Returns true if the given inside stream connector has no flow, which occurs
1133 when its min attribute >= 0."
1134 input Connector conn;
1135 input UnorderedMap<ComponentRef, Variable> variables;
1136 input Option<StreamFlowAlias.Replacements> replacements;
1137 output Boolean noFlow;
1138 algorithm
1139 1451 noFlow := isNoFlow(conn, true, variables, replacements);
1140 end isNoFlowInside;
1141
1142 function isNoFlow
1143 "Returns true if a given stream connector has no flow."
1144 input Connector element;
1145 input Boolean isInside;
1146 input UnorderedMap<ComponentRef, Variable> variables;
1147 input Option<StreamFlowAlias.Replacements> replacements;
1148 output Boolean noFlow;
1149 protected
1150 Option<Expression> attr_oexp;
1151 Expression attr_exp;
1152 Variable v;
1153 Boolean is_inside = isInside, negated;
1154 algorithm
1155 1488 (v, negated) := lookupFlowVarInConnector(element, variables, replacements);
1156
1157 // Flip the inside/outside of the connector if the flow variable was replaced
1158 // with a negative alias, i.e. flow = -alias.
1159
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1478 times.
1488 if negated then
1160 10 is_inside := not is_inside;
1161 end if;
1162
1163 // Check for min >= 0 if it's an inside connector and max <= 0 if it's an outside.
1164
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 1441 times.
1535 attr_oexp := lookupAttrInVar(if is_inside then "min" else "max", v);
1165
1166
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1488 times.
✓ Branch 2 taken 146 times.
✓ Branch 3 taken 1342 times.
1488 if isSome(attr_oexp) then
1167 1342 attr_exp := evaluateAttribute(Util.getOption(attr_oexp));
1168
2/2
✓ Branch 0 taken 1320 times.
✓ Branch 1 taken 22 times.
1342 noFlow := if is_inside then Expression.isNonNegative(attr_exp) else Expression.isNonPositive(attr_exp);
1169 else
1170 // If the variable doesn't have a min/max, check if it has a binding equation equal to zero instead.
1171 146 noFlow := Util.applyOptionOrDefault(Binding.getExpOpt(v.binding), Expression.isZero, false);
1172 end if;
1173 end isNoFlow;
1174
1175 function evaluateAttribute
1176 input output Expression exp;
1177 protected
1178 Variability var;
1179 algorithm
1180 1759 var := Expression.variability(exp);
1181
1182
3/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1745 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
1759 if var == Variability.PARAMETER and not Structural.isExpressionNotFixed(exp) then
1183 14 Structural.markExp(exp);
1184 var := Variability.STRUCTURAL_PARAMETER;
1185 end if;
1186
1187
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1745 times.
1745 if var <= Variability.STRUCTURAL_PARAMETER then
1188 1759 exp := Ceval.evalExp(exp);
1189 end if;
1190 end evaluateAttribute;
1191
1192 protected function evaluateActualStream
1193 "This function evaluates the actualStream operator for a component reference,
1194 given the connection sets."
1195 input ComponentRef streamCref;
1196 input ConnectionSets.Sets sets;
1197 input array<list<Connector>> setsArray;
1198 input UnorderedMap<ComponentRef, Variable> variables;
1199 input CardinalityTable.Table ctable;
1200 input Option<StreamFlowAlias.Replacements> replacements;
1201 output Expression exp;
1202 output ComponentRef flowCref;
1203 protected
1204 ComponentRef stream_cref;
1205 Integer flow_dir;
1206 Expression flow_exp, stream_exp, instream_exp;
1207 Operator op;
1208 algorithm
1209 62 stream_cref := ComponentRef.evaluateSubscripts(streamCref);
1210 62 flowCref := associatedFlowCref(stream_cref);
1211 62 flow_dir := evaluateFlowDirection(flowCref, variables);
1212
1213 // Select a branch if we know the flow direction, otherwise generate the whole
1214 // if-equation.
1215
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 60 times.
62 if flow_dir == 1 then
1216 2 exp := evaluateInStream(stream_cref, sets, setsArray, variables, ctable, replacements);
1217 elseif flow_dir == -1 then
1218 ✗ exp := Expression.fromCref(stream_cref);
1219 else
1220 // actualStream(stream_var) = if flow_var > 0 then inStream(stream_var) else stream_var);
1221 60 flow_exp := Expression.fromCref(flowCref);
1222 60 stream_exp := Expression.fromCref(stream_cref);
1223 60 instream_exp := evaluateInStream(stream_cref, sets, setsArray, variables, ctable, replacements);
1224 60 op := Operator.makeGreater(ComponentRef.nodeType(flowCref));
1225
1226 60 exp := Expression.IF(
1227 Type.REAL(),
1228 Expression.RELATION(flow_exp, op, Expression.REAL(0.0), -1),
1229 instream_exp, stream_exp);
1230 end if;
1231
1232
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 56 times.
62 if isSome(replacements) then
1233 6 exp := StreamFlowAlias.applyReplacementsInExp(Util.getOption(replacements), exp);
1234 end if;
1235 end evaluateActualStream;
1236
1237 function evaluateActualStreamMul
1238 "Handles expressions on the form flowCref * actualStream(streamCref) where
1239 flowCref is associated with streamCref."
1240 input Expression crefExp;
1241 input Expression actualStreamArg;
1242 input Operator op;
1243 input ConnectionSets.Sets sets;
1244 input array<list<Connector>> setsArray;
1245 input UnorderedMap<ComponentRef, Variable> variables;
1246 input CardinalityTable.Table ctable;
1247 input Option<StreamFlowAlias.Replacements> replacements;
1248 output Expression outExp;
1249 protected
1250 Expression e1, e2;
1251 ComponentRef cr, flow_cr;
1252 algorithm
1253
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 e1 as Expression.CREF(cref = cr) := evaluateOperators(crefExp, sets, setsArray, variables, ctable, replacements);
1254 38 (e2, flow_cr) := evaluateActualStream(Expression.toCref(actualStreamArg), sets, setsArray, variables, ctable, replacements);
1255 38 outExp := Expression.BINARY(e1, op, e2);
1256
1257 // Wrap the expression in smooth if the result would be flow_cr * (if flow_cr > 0 then ...)
1258 outExp := match e2
1259 35 case Expression.IF() guard ComponentRef.isEqual(cr, flow_cr) then makeSmoothCall(outExp, 0);
1260 else outExp;
1261 end match;
1262 end evaluateActualStreamMul;
1263
1264 function evaluateFlowDirection
1265 input ComponentRef flowCref;
1266 input UnorderedMap<ComponentRef, Variable> variables;
1267 output Integer direction = 0;
1268 protected
1269 Option<Expression> omin, omax;
1270 Real min_val, max_val;
1271 algorithm
1272 62 omin := lookupVarAttr(flowCref, "min", variables);
1273 62 omin := Util.applyOption(omin, function SimplifyExp.simplify(includeScope = false));
1274 62 omax := lookupVarAttr(flowCref, "max", variables);
1275 62 omax := Util.applyOption(omax, function SimplifyExp.simplify(includeScope = false));
1276
1277 direction := match (omin, omax)
1278 // No attributes, flow direction can't be decided.
1279 case (NONE(), NONE()) then 0;
1280 // Flow is positive if min is positive.
1281 case (SOME(Expression.REAL(min_val)), NONE())
1282 ✗ then if min_val >= 0 then 1 else 0;
1283 // Flow is negative if max is negative.
1284 case (NONE(), SOME(Expression.REAL(max_val)))
1285 ✗ then if max_val <= 0 then -1 else 0;
1286 // Flow is positive if both min and max are positive, negative if they are
1287 // both negative, otherwise undecideable.
1288 case (SOME(Expression.REAL(min_val)), SOME(Expression.REAL(max_val)))
1289
4/8
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 33 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
35 then
1290 if min_val >= 0 and max_val >= min_val then 1
1291 elseif max_val <= 0 and min_val <= max_val then -1
1292 else 0;
1293 // Flow is undecideable if either attribute is not a constant Real value.
1294 else 0;
1295 end match;
1296 end evaluateFlowDirection;
1297
1298 function makeSmoothCall
1299 "Creates a smooth(order, arg) call."
1300 input Expression arg;
1301 input Integer order;
1302 output Expression callExp;
1303 algorithm
1304 70 callExp := Expression.CALL(Call.makeTypedCall(NFBuiltinFuncs.SMOOTH,
1305 {Expression.INTEGER(order), arg}, Expression.variability(arg), Purity.PURE,
1306 Expression.typeOf(arg)));
1307 end makeSmoothCall;
1308
1309 protected function removeStreamSetElement
1310 "This function removes the given cref from a connection set."
1311 input ComponentRef cref;
1312 input output list<Connector> elements;
1313 algorithm
1314 618 elements := List.deleteMemberOnTrue(cref, elements, compareCrefStreamSet);
1315 end removeStreamSetElement;
1316
1317 protected function compareCrefStreamSet
1318 "Helper function to removeStreamSetElement. Checks if the cref in a stream set
1319 element matches the given cref."
1320 input ComponentRef cref;
1321 input Connector element;
1322 output Boolean matches;
1323 algorithm
1324 1062 matches := ComponentRef.isEqual(cref, element.name);
1325 end compareCrefStreamSet;
1326
1327 function associatedFlowCref
1328 "Returns the flow cref that's declared in the same connector as the given
1329 stream cref."
1330 input ComponentRef streamCref;
1331 output ComponentRef flowCref;
1332 protected
1333 Type ty;
1334 ComponentRef rest_cr;
1335 NFInstNode.ScopeRef flow_node;
1336 algorithm
1337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7638 times.
7638 ComponentRef.CREF(ty = ty, restCref = rest_cr) := streamCref;
1338
1339 flowCref := match Type.arrayElementType(ty)
1340 // A connector with a single flow, append the flow node to the cref and return it.
1341 case Type.COMPLEX(complexTy = ComplexType.CONNECTOR(flows = {flow_node}))
1342 3819 then ComponentRef.prefixCref(InstNode.borrow(flow_node),
1343 InstNode.getType(InstNode.borrow(flow_node)), {}, streamCref);
1344
1345 // Otherwise, remove the first part of the cref and try again.
1346 3819 else associatedFlowCref(rest_cr);
1347 end match;
1348 end associatedFlowCref;
1349
1350 function lookupVar
1351 input ComponentRef varName;
1352 input UnorderedMap<ComponentRef, Variable> variables;
1353 output Variable var;
1354 protected
1355 Option<Variable> ovar;
1356 algorithm
1357 2971 ovar := UnorderedMap.get(varName, variables);
1358
1359
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2971 times.
✓ Branch 2 taken 89 times.
✓ Branch 3 taken 2882 times.
2971 if isNone(ovar) then
1360 89 ovar := UnorderedMap.get(ComponentRef.stripSubscriptsAll(varName), variables);
1361 end if;
1362
1363
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2971 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2971 times.
2971 if isNone(ovar) then
1364 ✗ Error.addInternalError(getInstanceName() + " could not find the variable " +
1365 ComponentRef.toString(varName) + "\n", sourceInfo());
1366 end if;
1367
1368
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2971 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2971 times.
2971 SOME(var) := ovar;
1369 end lookupVar;
1370
1371 function lookupVarAttr
1372 input ComponentRef varName;
1373 input String attrName;
1374 input UnorderedMap<ComponentRef, Variable> variables;
1375 output Option<Expression> attrValue;
1376 protected
1377 Variable var;
1378 Binding binding;
1379 algorithm
1380 1028 var := lookupVar(varName, variables);
1381 1028 binding := Variable.lookupTypeAttribute(attrName, var);
1382 1028 attrValue := Binding.typedExp(binding);
1383 end lookupVarAttr;
1384
1385 function lookupAttrInVar
1386 input String attrName;
1387 input Variable var;
1388 output Option<Expression> attrValue;
1389 protected
1390 Binding binding;
1391 algorithm
1392 1943 binding := Variable.lookupTypeAttribute(attrName, var);
1393 1943 attrValue := Binding.typedExp(binding);
1394 end lookupAttrInVar;
1395
1396 function lookupFlowVarInConnector
1397 "Looks up the associated flow variable for a stream connector, and whether is
1398 was replaced with a negative alias or not (flow = -alias)."
1399 input Connector conn;
1400 input UnorderedMap<ComponentRef, Variable> variables;
1401 input Option<StreamFlowAlias.Replacements> replacements;
1402 output Variable var;
1403 output Boolean negated;
1404 protected
1405 Expression flow_exp;
1406 ComponentRef flow_name;
1407 algorithm
1408 1943 flow_exp := flowExp(conn);
1409
1410 // Replace the flow variable with its alias if it has one.
1411
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1943 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 1922 times.
1943 if isSome(replacements) then
1412 21 flow_exp := StreamFlowAlias.applyReplacementsInExp(Util.getOption(replacements), flow_exp);
1413 end if;
1414
1415 // flow_exp is either flow_name or -flow_name.
1416 1943 (flow_name, negated) := expFlowName(flow_exp);
1417 1943 var := lookupVar(flow_name, variables);
1418 end lookupFlowVarInConnector;
1419
1420 function expFlowName
1421 input Expression exp;
1422 output ComponentRef name;
1423 output Boolean negated;
1424 algorithm
1425 (name, negated) := match exp
1426 1943 case Expression.CREF() then (exp.cref, false);
1427 case Expression.UNARY()
1428 algorithm
1429 15 (name, negated) := expFlowName(exp.exp);
1430 15 then
1431 (name, not negated);
1432 end match;
1433 end expFlowName;
1434
1435 annotation(__OpenModelica_Interface="nf_frontend");
1436 end NFConnectEquations;
1437