Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 47.2% 159 / 0 / 337
Functions: -% 0 / 1 / 1
Branches: 44.6% 123 / 0 / 276

OMCompiler/Compiler/NFFrontEnd/NFOCConnectionGraph.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 NFOCConnectionGraph
37 " file: NFOCConnectionGraph.mo
38 package: NFOCConnectionGraph
39 description: Constant propagation of expressions
40
41
42 This module contains a connection breaking algorithm and
43 related data structures. The input of the algorithm is
44 collected to NFOCConnectionGraph record during instantiation.
45 The entry point to the algorithm is findResultGraph.
46
47 The algorithm is implemented using a disjoint-set
48 data structure that represents the components of
49 elements so far connected.
50 Each component has an unique canonical element.
51 The data structure is implemented by a hash table, that
52 contains an entry for each non-canonical element so that
53 a path beginning from some element eventually ends to the
54 canonical element of the same component.
55
56 Roots are represented as connections to dummy root
57 element. In this way, all elements will be in the
58 same component after the algorithm finishes assuming
59 that the model is valid.
60
61 TODO! FIXME! adrpo 2014-10-05
62 - non standard operators: Connections.uniqueRoot and Connections.uniqueRootIndices are only partially implemented
63 - Connections.uniqueRoot currently does nothing, only collects information
64 - Connections.uniqueRootIndices needs to be implemented, it returns an array of ones (1) of size of first input
65 - See specification for these here (Modelica_StateGraph2):
66 https://github.com/modelica/Modelica_StateGraph2 and
67 https://trac.modelica.org/Modelica/ticket/984 and
68 http://www.ep.liu.se/ecp/043/041/ecp09430108.pdf
69 - any takers for the actual implementation? :)
70
71 "
72
73 public
74 import FlatModel = NFFlatModel;
75 import ComponentRef = NFComponentRef;
76 import Equation = NFEquation;
77 import NFConnections;
78 import Variable = NFVariable;
79
80 type FlatEdge = NFConnections.BrokenEdge
81 "a tuple with two crefs and equation(s) for calling the equalityConstraint function call";
82 type FlatEdges = NFConnections.BrokenEdges
83 "a lit of broken edges";
84
85
86 protected
87 import Absyn;
88 import DAE;
89 import NFBuiltin;
90 import Binding = NFBinding;
91 import Call = NFCall;
92 import Ceval = NFCeval;
93 import Class = NFClass;
94 import Dimension = NFDimension;
95 import DisjointSets;
96 import NFFunction.Function;
97 import NFInstNode.InstNode;
98 import NFInstNode;
99 import Operator = NFOperator;
100 import NFOperator.Op;
101 import DAE.Connect;
102 import Expression = NFExpression;
103 import Type = NFType;
104 import MetaModelica.Dangerous.listReverseInPlace;
105 import Connector = NFConnector;
106 import ElementSource;
107 import Typing = NFTyping;
108 import NFPrefixes.Variability;
109 import Error;
110 import Connections = NFConnections;
111 import Connection = NFConnection;
112 import InstContext = NFInstContext;
113 import UnorderedMap;
114
115 type Edge = tuple<ComponentRef,ComponentRef> "an edge is a tuple with two component references";
116 type Edges = list<Edge> "A list of edges";
117
118 type DefiniteRoot = ComponentRef "root defined with Connection.root";
119 type DefiniteRoots = list<ComponentRef> "roots defined with Connection.root";
120 type UniqueRoots = list<tuple<ComponentRef,Expression>> "roots defined with Connection.uniqueRoot";
121
122 type PotentialRoot = tuple<ComponentRef,Real> "potential root defined with Connections.potentialRoot";
123 type PotentialRoots = list<tuple<ComponentRef,Real>> "potential roots defined with Connections.potentialRoot";
124
125 uniontype NFOCConnectionGraph "Input structure for connection breaking algorithm. It is collected during instantiation phase."
126 record GRAPH
127 Boolean updateGraph;
128 DefiniteRoots definiteRoots "Roots defined with Connection.root";
129 PotentialRoots potentialRoots "Roots defined with Connection.potentialRoot";
130 UniqueRoots uniqueRoots "Roots defined with Connection.uniqueRoot";
131 Edges branches "Edges defined with Connection.branch";
132 FlatEdges connections "Edges defined with connect statement";
133 end GRAPH;
134 end NFOCConnectionGraph;
135
136 constant NFOCConnectionGraph EMPTY = GRAPH( true, {}, {}, {}, {}, {} ) "Initial connection graph with no edges in it.";
137
138 type ConnectionsOperator = enumeration(
139 BRANCH,
140 ROOT,
141 POTENTIAL_ROOT,
142 IS_ROOT,
143 ROOTED,
144 UNIQUE_ROOT,
145 UNIQUE_ROOT_INDICES,
146 NOT_OPERATOR
147 );
148
149 type CrefCrefTable = UnorderedMap<ComponentRef, ComponentRef>;
150 type CrefIndexTable = UnorderedMap<ComponentRef, Integer>;
151 type CrefRootsTable = UnorderedMap<ComponentRef, DefiniteRoots>;
152
153 package CrefSets
154 extends DisjointSets(redeclare type Entry = ComponentRef);
155
156 redeclare function extends EntryHash
157 algorithm
158 6777 hash := ComponentRef.hash(entry);
159 end EntryHash;
160
161 redeclare function extends EntryEqual
162 algorithm
163 1283 isEqual := ComponentRef.isEqual(entry1, entry2);
164 end EntryEqual;
165
166 redeclare function extends EntryString
167 algorithm
168 ✗ str := ComponentRef.toString(entry);
169 end EntryString;
170 end CrefSets;
171
172 public
173 partial function IsDeletedFn
174 input ComponentRef cref;
175 output Boolean res;
176 end IsDeletedFn;
177
178 function handleOverconstrainedConnections
179 "@author: adrpo
180 goes over all equations from the FlatModel and:
181 1. builds the overconstrained connection graph from:
182 - connect, Connections.branch
183 - Connections.root, Connections.potentialRoot
184 2. Breaks the overconstrained connection graph
185 and replaces the broken connects with a call
186 to the equalityConstraint function
187 3. using the graph evaluates:
188 - Connections.isRoot, Connections.rooted, rooted
189 4. partially handles non-standard
190 - Connections.uniqueRoot
191 - Connections.uniqueRootIndices"
192 input output FlatModel flatModel;
193 input Connections conns;
194 input IsDeletedFn isDeleted;
195 output FlatEdges broken;
196 protected
197 NFOCConnectionGraph graph = EMPTY;
198 FlatEdges connected;
199 list<Equation> eql;
200 Boolean print_trace = Flags.isSet(Flags.CGRAPH);
201 algorithm
202 // Add roots and branches from the model to the graph.
203 76 graph := addBreakableBranches(conns.connections, isDeleted, print_trace, graph);
204
4/4
✓ Branch 0 taken 27096 times.
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 27096 times.
✓ Branch 3 taken 76 times.
27172 (eql, graph) := addRootsAndBranches(List.flatten(list(expandArrayOperatorCall(eq) for eq in flatModel.equations)), print_trace, graph);
205 76 flatModel.equations := eql;
206
207 // now we have the graph, remove the broken connects and evaluate the equation operators
208 76 (flatModel, connected, broken) := handleOverconstrainedConnections_dispatch(graph, flatModel);
209
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 flatModel.equations := removeBrokenConnects(flatModel.equations, connected, broken, isDeleted);
210 end handleOverconstrainedConnections;
211
212 function handleOverconstrainedArrayConnections
213 "handleOverconstrainedConnections for the array connection handler, where the
214 connect equations and the equations of arrays of components stay for loops:
215 the graph is built from a copy of the equations with the for loops unrolled
216 (unrolledEquations, conns collected from them), the Connections.* operators
217 are evaluated inside the for loops of the model and Connections.root, branch
218 and potentialRoot are removed from them. The connect equations stay for the
219 array handler, broken connections are returned and not removed."
220 input output FlatModel flatModel;
221 input list<Equation> unrolledEquations;
222 input Connections conns;
223 input IsDeletedFn isDeleted;
224 output FlatEdges broken;
225 protected
226 NFOCConnectionGraph graph = EMPTY;
227 FlatEdges connected;
228 list<ComponentRef> roots;
229 CrefIndexTable rooted;
230 Boolean print_trace = Flags.isSet(Flags.CGRAPH);
231 algorithm
232 ✗ graph := addBreakableBranches(conns.connections, isDeleted, print_trace, graph);
233 ✗ (_, graph) := addRootsAndBranches(List.flatten(list(expandArrayOperatorCall(eq) for eq in unrolledEquations)), print_trace, graph);
234 ✗ flatModel.equations := removeConnectionsOperatorCalls(flatModel.equations);
235
236 ✗ (roots, connected, broken) := findResultGraph(graph, FlatModel.fullName(flatModel));
237 ✗ if print_trace then
238 ✗ print("Array connections, roots: " + stringDelimitList(List.map(roots, ComponentRef.toString), ", ") + "\n");
239 ✗ print("Branches: " + intString(listLength(getBranches(graph))) + ", connections: " + intString(listLength(getConnections(graph)))
240 + ", potential roots: " + intString(listLength(getPotentialRoots(graph))) + "\n");
241 end if;
242 ✗ rooted := buildRootedTable(roots, connected, graph);
243 ✗ flatModel.variables := list(evalConnectionsOperatorsVar(roots, rooted, graph, v) for v in flatModel.variables);
244 flatModel.equations := list(Equation.mapExp(eq,
245 function evaluateOperators(rooted = rooted, roots = roots, graph = graph, info = Equation.info(eq)))
246 for eq in flatModel.equations);
247 flatModel.initialEquations := list(Equation.mapExp(eq,
248 function evaluateOperators(rooted = rooted, roots = roots, graph = graph, info = Equation.info(eq)))
249 for eq in flatModel.initialEquations);
250 end handleOverconstrainedArrayConnections;
251
252 protected
253 function expandArrayOperatorCall
254 "Connections.root, potentialRoot, uniqueRoot and branch of an array of
255 components (e.g. Connections.branch(line.a.theta, line.b.theta) for line[N])
256 as one call for each component, like for scalarized equations."
257 input Equation eq;
258 output list<Equation> eqs;
259 protected
260 Call call;
261 list<Expression> args, rest;
262 list<list<ComponentRef>> elems;
263 ComponentRef cref;
264 Integer n;
265 algorithm
266 eqs := match eq
267 case Equation.NORETCALL(exp = Expression.CALL(call = call as Call.TYPED_CALL(arguments = args)))
268 guard isGraphOperator(eq) and List.any(args, isComponentArrayCref)
269 algorithm
270 // the crefs arguments element by element, the other ones (priority) as they are
271
4/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 8 times.
38 elems := list(match a case Expression.CREF() then componentElements(a.cref); else {}; end match for a in args);
272
4/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 8 times.
24 n := max(listLength(e) for e in elems);
273 eqs := {};
274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 for i in n:-1:1 loop
275 ✗ call.arguments := list(match a
276 case Expression.CREF() algorithm
277 ✗ cref := listGet(e, i);
278 ✗ then Expression.CREF(ComponentRef.getSubscriptedType(cref), cref);
279 else a; end match threaded for a in args, e in elems);
280 ✗ eqs := Equation.NORETCALL(Expression.CALL(call), eq.scope, eq.source) :: eqs;
281 end for;
282 then eqs;
283 else {eq};
284 end match;
285 end expandArrayOperatorCall;
286
287 function isComponentArrayCref
288 "true for a cref of an array of components, e.g. line.a.theta for line[N],
289 also if the array has a single element"
290 input Expression exp;
291 output Boolean b;
292 protected
293 list<ComponentRef> elems;
294 algorithm
295 b := match exp
296 case Expression.CREF()
297 algorithm
298 1604 elems := componentElements(exp.cref);
299
3/4
✓ Branch 1 taken 1596 times.
✓ Branch 2 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1596 times.
1604 then listLength(elems) <> 1 or not ComponentRef.isEqual(listHead(elems), ComponentRef.stripSubscripts(exp.cref));
300 else false;
301 end match;
302 end isComponentArrayCref;
303
304 function componentElements
305 "The elements of the arrays of components a cref refers to, without the
306 dimensions of the variable itself (theta[2] stays one variable)."
307 input ComponentRef cref;
308 output list<ComponentRef> elems;
309 algorithm
310
4/4
✓ Branch 2 taken 1613 times.
✓ Branch 3 taken 1618 times.
✓ Branch 4 taken 1613 times.
✓ Branch 5 taken 1618 times.
3231 elems := list(ComponentRef.stripSubscripts(c) for c in listReverse(ComponentRef.scalarizeAll(cref, false)));
311 1618 elems := uniqueInOrder(elems);
312 end componentElements;
313
314 function uniqueInOrder
315 "the crefs without duplicates, in their order (the elements of the arguments
316 of an operator call are paired by position)"
317 input list<ComponentRef> crefs;
318 output list<ComponentRef> unique = {};
319 protected
320 UnorderedSet<ComponentRef> seen = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
321 algorithm
322
2/2
✓ Branch 0 taken 1613 times.
✓ Branch 1 taken 1618 times.
3231 for cr in crefs loop
323
2/2
✓ Branch 1 taken 1596 times.
✓ Branch 2 taken 17 times.
1613 if not UnorderedSet.contains(cr, seen) then
324 1596 UnorderedSet.add(cr, seen);
325 unique := cr :: unique;
326 end if;
327 end for;
328 1618 unique := listReverse(unique);
329 end uniqueInOrder;
330
331 function removeConnectionsOperatorCalls
332 "Removes Connections.root, branch, potentialRoot and uniqueRoot calls, also
333 inside for loops; a for loop that has nothing else is removed."
334 input list<Equation> equations;
335 output list<Equation> outEquations = {};
336 algorithm
337 ✗ for eq in equations loop
338 outEquations := match eq
339 case Equation.NORETCALL(exp = Expression.CALL(call = Call.TYPED_CALL()))
340 guard isGraphOperator(eq)
341 then outEquations;
342 case Equation.FOR() algorithm
343 ✗ eq.body := removeConnectionsOperatorCalls(eq.body);
344 ✗ then if listEmpty(eq.body) then outEquations else eq :: outEquations;
345 else eq :: outEquations;
346 end match;
347 end for;
348 ✗ outEquations := listReverseInPlace(outEquations);
349 end removeConnectionsOperatorCalls;
350
351 function isGraphOperator
352 input Equation eq;
353 output Boolean b;
354 protected
355 Function fn;
356 algorithm
357
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 983 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 983 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 983 times.
983 Equation.NORETCALL(exp = Expression.CALL(call = Call.TYPED_CALL(fn = fn))) := eq;
358 b := match identifyConnectionsOperator(Function.name(fn))
359 case ConnectionsOperator.ROOT then true;
360 case ConnectionsOperator.POTENTIAL_ROOT then true;
361 case ConnectionsOperator.UNIQUE_ROOT then true;
362 case ConnectionsOperator.BRANCH then true;
363 else false;
364 end match;
365 end isGraphOperator;
366
367
368 function addBreakableBranches
369 "Adds breakable branches, i.e. normal connections, to the graph."
370 input list<Connection> connections;
371 input IsDeletedFn isDeleted;
372 input Boolean printTrace;
373 input output NFOCConnectionGraph graph;
374 protected
375 CrefSets.Sets breakable;
376 Connector c1, c2;
377 list<ComponentRef> lhs_crefs, rhs_crefs;
378 ComponentRef rhs;
379 Integer lhs_set, rhs_set;
380 algorithm
381 // Disjoint sets used to check for redundant breakable branches.
382 76 breakable := CrefSets.emptySets(3);
383
384 // Add breakable branches to the graph.
385
2/2
✓ Branch 0 taken 2942 times.
✓ Branch 1 taken 76 times.
3018 for conn in connections loop
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2942 times.
2942 Connection.CONNECTION(lhs = c1, rhs = c2) := conn;
387
388 2942 lhs_crefs := getOverconstrainedCrefs(c1, isDeleted);
389 2942 rhs_crefs := getOverconstrainedCrefs(c2, isDeleted);
390
391
2/2
✓ Branch 0 taken 2015 times.
✓ Branch 1 taken 2942 times.
4957 for lhs in lhs_crefs loop
392
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2015 times.
2015 rhs :: rhs_crefs := rhs_crefs;
393 2015 (lhs_set, breakable) := CrefSets.findSet(lhs, breakable);
394 2015 (rhs_set, breakable) := CrefSets.findSet(rhs, breakable);
395
396 // Add the breakable branch to the graph if the connectors are not already
397 // in the same set, otherwise the branch is redundant and should be ignored.
398
1/2
✓ Branch 0 taken 2015 times.
✗ Branch 1 not taken.
2015 if lhs_set <> rhs_set then
399 2015 graph := addConnection(lhs, rhs, c1.source, printTrace, graph);
400 // Merge the sets of the two connectors.
401 2015 breakable := CrefSets.union(lhs_set, rhs_set, breakable);
402 end if;
403 end for;
404 end for;
405 end addBreakableBranches;
406
407 function addRootsAndBranches
408 "Adds roots and nonbreakable branches in a list of equations to the graph."
409 input list<Equation> equations;
410 input Boolean printTrace;
411 output list<Equation> outEquations = {};
412 input output NFOCConnectionGraph graph;
413 protected
414 Call call;
415 list<Expression> args;
416 Expression arg1, arg2, root, msg;
417 ComponentRef cref, lhs, rhs;
418 Integer priority;
419 algorithm
420
2/2
✓ Branch 0 taken 27088 times.
✓ Branch 1 taken 76 times.
27164 for eq in equations loop
421 outEquations := match eq
422 case Equation.NORETCALL(exp = Expression.CALL(call as Call.TYPED_CALL(arguments = args)))
423 then match identifyConnectionsOperator(Function.name(call.fn))
424 case ConnectionsOperator.ROOT
425 algorithm
426
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 131 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 131 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 131 times.
131 {Expression.CREF(cref = cref)} := args;
427 131 graph := addDefiniteRoot(cref, printTrace, graph);
428 then outEquations;
429
430 case ConnectionsOperator.POTENTIAL_ROOT
431 algorithm
432
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 224 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 224 times.
224 {arg1, arg2} := args;
433
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 Expression.CREF(cref = cref) := arg1;
434
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 224 times.
224 Expression.INTEGER(value = priority) := Ceval.evalExp(arg2);
435 224 graph := addPotentialRoot(cref, priority, printTrace, graph);
436 then
437 outEquations;
438
439 case ConnectionsOperator.UNIQUE_ROOT
440 algorithm
441 graph := match args
442 case {root as Expression.CREF(cref = cref)}
443 ✗ then addUniqueRoots(root, Expression.STRING(""), printTrace, graph);
444 case {root as Expression.CREF(cref = cref), msg}
445 ✗ then addUniqueRoots(root, msg, printTrace, graph);
446 end match;
447 then outEquations;
448
449 case ConnectionsOperator.BRANCH
450 algorithm
451
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 620 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 620 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 620 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 620 times.
620 {Expression.CREF(cref = lhs), Expression.CREF(cref = rhs)} := args;
452 620 graph := addBranch(lhs, rhs, printTrace, graph);
453 then outEquations;
454
455 else eq :: outEquations;
456 end match;
457
458 else eq::outEquations;
459 end match;
460 end for;
461
462 76 outEquations := listReverseInPlace(outEquations);
463 end addRootsAndBranches;
464
465 function generateEqualityConstraintEquation
466 input ComponentRef lhs;
467 input ComponentRef rhs;
468 input DAE.ElementSource source;
469 output Equation equalityConstraintEq;
470 protected
471 InstContext.Type context;
472 ComponentRef fcref_rhs, fcref_lhs;
473 InstNode fn_node_rhs, fn_node_lhs;
474 Expression exp_rhs, exp_lhs;
475 Type ty;
476 SourceInfo info = ElementSource.getInfo(source);
477 algorithm
478 context := intBitOr(NFInstContext.EQUATION, NFInstContext.CONNECT);
479
480 32 fcref_rhs := Function.lookupFunctionSimple("equalityConstraint", InstNode.classScope(ComponentRef.node(lhs)), context);
481 32 (fcref_rhs, fn_node_rhs) := Function.instFunctionRef(fcref_rhs, context, Absyn.dummyInfo);
482 96 exp_rhs := Expression.CALL(Call.UNTYPED_CALL(fcref_rhs, {Expression.fromCref(lhs), Expression.fromCref(rhs)}, {}, InstNode.scopeRef(fn_node_rhs)));
483 32 (exp_rhs, ty) := Typing.typeExp(exp_rhs, context, info);
484
485 32 fcref_lhs := Function.lookupFunctionSimple("fill", InstNode.topScope(ComponentRef.node(lhs)), context);
486 32 (fcref_lhs, fn_node_lhs) := Function.instFunctionRef(fcref_lhs, context, Absyn.dummyInfo);
487
4/4
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 32 times.
✓ Branch 4 taken 32 times.
64 exp_lhs := Expression.CALL(Call.UNTYPED_CALL(fcref_lhs, Expression.REAL(0.0)::list(Dimension.sizeExp(d) for d in Type.arrayDims(ty)), {}, InstNode.scopeRef(fn_node_lhs)));
488 32 (exp_lhs, ty) := Typing.typeExp(exp_lhs, context, info);
489
490 32 equalityConstraintEq := Equation.makeEquality(exp_rhs, exp_lhs, ty, source);
491 end generateEqualityConstraintEquation;
492
493 function getOverconstrainedCrefs
494 input Connector conn;
495 input IsDeletedFn isDeleted;
496 output list<ComponentRef> crefs;
497 protected
498 list<Connector> conns;
499 algorithm
500 5884 conns := Connector.split(conn);
501 5884 conns := List.mapFlat(conns, Connector.scalarizePrefix);
502
8/10
✓ Branch 0 taken 23610 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 23610 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 15852 times.
✓ Branch 8 taken 7758 times.
✓ Branch 9 taken 23610 times.
✓ Branch 10 taken 5884 times.
✓ Branch 11 taken 7758 times.
✓ Branch 12 taken 5884 times.
29494 crefs := list(getOverconstrainedCref(c.name) for c
503 guard not isDeleted(c.name) and isOverconstrainedCref(c.name) in conns);
504 5884 crefs := List.uniqueOnTrue(crefs, ComponentRef.isEqual);
505 end getOverconstrainedCrefs;
506
507 function isOverconstrainedCref
508 input ComponentRef cref;
509 output Boolean b = false;
510 protected
511 InstNode node;
512 ComponentRef rest;
513 algorithm
514 b := match cref
515 case ComponentRef.CREF(origin = NFComponentRef.Origin.CREF, restCref = rest)
516
4/4
✓ Branch 3 taken 52602 times.
✓ Branch 4 taken 7758 times.
✓ Branch 6 taken 7722 times.
✓ Branch 7 taken 44880 times.
60360 then Class.isOverdetermined(InstNode.getClass(ComponentRef.node(cref))) or isOverconstrainedCref(rest);
517 else false;
518 end match;
519 end isOverconstrainedCref;
520
521 function getOverconstrainedCref
522 input ComponentRef cref;
523 output ComponentRef c;
524 protected
525 InstNode node;
526 ComponentRef rest;
527 algorithm
528 c := match cref
529 case ComponentRef.CREF(origin = NFComponentRef.Origin.CREF, restCref = rest)
530
2/2
✓ Branch 3 taken 7758 times.
✓ Branch 4 taken 7722 times.
15480 then
531 if Class.isOverdetermined(InstNode.getClass(ComponentRef.node(cref))) then cref else getOverconstrainedCref(rest);
532 end match;
533 end getOverconstrainedCref;
534
535 function handleOverconstrainedConnections_dispatch
536 "author: adrpo
537 this function gets the connection graph and the existing DAE and:
538 - returns a list of broken connects and one list of connected connects
539 - evaluates Connections.isRoot in the input DAE
540 - evaluates Connections.uniqueRootIndices in the input DAE
541 - evaluates the rooted operator in the input DAE"
542 input NFOCConnectionGraph graph;
543 input output FlatModel flatModel;
544 output FlatEdges connected;
545 output FlatEdges broken;
546 protected
547 list<ComponentRef> roots;
548 CrefIndexTable rooted;
549 algorithm
550 try
551
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 76 times.
76 if Flags.isSet(Flags.CGRAPH) then
552 ✗ print("Summary:\n\t" +
553 "Nr Roots: " + intString(listLength(getDefiniteRoots(graph))) + "\n\t" +
554 "Nr Potential Roots: " + intString(listLength(getPotentialRoots(graph))) + "\n\t" +
555 "Nr Unique Roots: " + intString(listLength(getUniqueRoots(graph))) + "\n\t" +
556 "Nr Branches: " + intString(listLength(getBranches(graph))) + "\n\t" +
557 "Nr Connections: " + intString(listLength(getConnections(graph))) + "\n");
558 end if;
559
560 76 (roots, connected, broken) := findResultGraph(graph, FlatModel.fullName(flatModel));
561
562
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 76 times.
76 if Flags.isSet(Flags.CGRAPH) then
563 ✗ print("Roots: " + stringDelimitList(List.map(roots, ComponentRef.toString), ", ") + "\n");
564 ✗ print("Broken connections: " + stringDelimitList(List.map1(broken, printConnectionStr, "broken"), ", ") + "\n");
565 ✗ print("Allowed connections: " + stringDelimitList(List.map1(connected, printConnectionStr, "allowed"), ", ") + "\n");
566 end if;
567
568 76 rooted := buildRootedTable(roots, connected, graph);
569
4/4
✓ Branch 0 taken 71434 times.
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 71434 times.
✓ Branch 3 taken 76 times.
71586 flatModel.variables := list(evalConnectionsOperatorsVar(roots, rooted, graph, v) for v in flatModel.variables);
570 76 flatModel.equations := evalConnectionsOperatorsEqs(roots, rooted, graph, flatModel.equations);
571
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 flatModel.initialEquations := evalConnectionsOperatorsEqs(roots, rooted, graph, flatModel.initialEquations);
572 else
573 ✗ true := Flags.isSet(Flags.CGRAPH);
574 ✗ print("- NFOCConnectionGraph.handleOverconstrainedConnections failed for model: " + FlatModel.fullName(flatModel) + "\n");
575 ✗ fail();
576 end try;
577 end handleOverconstrainedConnections_dispatch;
578
579 function addDefiniteRoot
580 "Adds a new definite root to NFOCConnectionGraph"
581 input ComponentRef root;
582 input Boolean printTrace;
583 input output NFOCConnectionGraph graph;
584 algorithm
585
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 131 times.
131 if printTrace then
586 ✗ print("- NFOCConnectionGraph.addDefiniteRoot(" + ComponentRef.toString(root) + ")\n");
587 end if;
588
589 131 graph.definiteRoots := root :: graph.definiteRoots;
590 end addDefiniteRoot;
591
592 function addPotentialRoot
593 "Adds a new potential root to NFOCConnectionGraph"
594 input ComponentRef root;
595 input Real priority;
596 input Boolean printTrace;
597 input output NFOCConnectionGraph graph;
598 algorithm
599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 if printTrace then
600 ✗ print("- NFOCConnectionGraph.addPotentialRoot(" + ComponentRef.toString(root) +
601 ", " + realString(priority) + ")" + "\n");
602 end if;
603
604 224 graph.potentialRoots := (root, priority) :: graph.potentialRoots;
605 end addPotentialRoot;
606
607 function addUniqueRoots
608 "Adds a new unique root to NFOCConnectionGraph"
609 input Expression roots;
610 input Expression message;
611 input Boolean printTrace;
612 input output NFOCConnectionGraph graph;
613 protected
614 UniqueRoots unique_roots = graph.uniqueRoots;
615 algorithm
616 ✗ for root in Expression.arrayScalarElements(roots) loop
617 unique_roots := match root
618 case Expression.CREF()
619 algorithm
620 ✗ if printTrace then
621 ✗ print("- NFOCConnectionGraph.addUniqueRoots(" + Expression.toString(root) +
622 ", " + Expression.toString(message) + ")\n");
623 end if;
624 ✗ then
625 (root.cref, message) :: unique_roots;
626
627 else
628 algorithm
629 // TODO! FIXME! print some meaningful error message here that the input is not an array of roots or a cref
630 then
631 unique_roots;
632
633 end match;
634 end for;
635 end addUniqueRoots;
636
637 function addBranch
638 input ComponentRef ref1;
639 input ComponentRef ref2;
640 input Boolean printTrace;
641 input output NFOCConnectionGraph graph;
642 algorithm
643
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 620 times.
620 if printTrace then
644 ✗ print("- NFOCConnectionGraph.addBranch(" + ComponentRef.toString(ref1) + ", " + ComponentRef.toString(ref2) + ")\n");
645 end if;
646
647 620 graph.branches := (ref1, ref2) :: graph.branches;
648 end addBranch;
649
650 function addConnection
651 "Adds a new connection to NFOCConnectionGraph"
652 input ComponentRef ref1;
653 input ComponentRef ref2;
654 input DAE.ElementSource source;
655 input Boolean printTrace;
656 input output NFOCConnectionGraph graph;
657 algorithm
658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2015 times.
2015 if printTrace then
659 ✗ print("- NFOCConnectionGraph.addConnection(" + ComponentRef.toString(ref1) + ", " + ComponentRef.toString(ref2) + ")\n");
660 end if;
661
662 2015 graph.connections := FlatEdge.BROKEN_EDGE(ref1, ref2, source, {}) :: graph.connections;
663 end addConnection;
664
665 // ************************************* //
666 // ********* protected section ********* //
667 // ************************************* //
668
669 protected import Debug;
670 protected import Flags;
671 protected import List;
672 protected import Util;
673 protected import System;
674 protected import IOStream;
675 protected import Settings;
676
677 protected function canonical
678 "Returns the canonical element of the component where input element belongs to.
679 See explanation at the top of file."
680 input CrefCrefTable inPartition;
681 input ComponentRef inRef;
682 output ComponentRef outCanonical;
683 protected
684 Option<ComponentRef> cref_opt;
685
686 algorithm
687 11538 cref_opt := UnorderedMap.get(inRef, inPartition);
688
689 outCanonical := match cref_opt
690 5820 case SOME(outCanonical) then canonical(inPartition, outCanonical);
691 else inRef;
692 end match;
693 end canonical;
694
695 protected function areInSameComponent
696 "Tells whether the elements belong to the same component.
697 See explanation at the top of file."
698 input CrefCrefTable partition;
699 input ComponentRef ref1;
700 input ComponentRef ref2;
701 output Boolean outResult;
702 algorithm
703 ✗ outResult := ComponentRef.isEqual(canonical(partition, ref1),
704 canonical(partition, ref2));
705 end areInSameComponent;
706
707
708 protected function connectBranchComponents
709 "Tries to connect two components whose elements are given. Depending
710 on wheter the connection success or not (i.e are the components already
711 connected), adds either inConnectionDae or inBreakDae to the list of
712 DAE elements."
713 input CrefCrefTable partition;
714 input ComponentRef ref1;
715 input ComponentRef ref2;
716 algorithm
717 620 connectCanonicalComponents(partition,
718 canonical(partition, ref1), canonical(partition, ref2));
719 end connectBranchComponents;
720
721 protected function connectComponents
722 "Tries to connect two components whose elements are given. Depending
723 on wheter the connection success or not (i.e are the components already
724 connected), adds either inConnectionDae or inBreakDae to the list of
725 DAE elements."
726 input CrefCrefTable partition;
727 input FlatEdge edge;
728 output FlatEdges outConnectedConnections;
729 output FlatEdges outBrokenConnections;
730 protected
731 ComponentRef canon1, canon2;
732 Equation eq;
733 algorithm
734 try
735 2015 canon1 := canonical(partition, edge.lhs);
736 2015 canon2 := canonical(partition, edge.rhs);
737
2/2
✓ Branch 1 taken 1983 times.
✓ Branch 2 taken 32 times.
2015 false := connectCanonicalComponents(partition, canon1, canon2);
738
739 // debug print
740
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
32 if Flags.isSet(Flags.CGRAPH) then
741 ✗ Debug.trace("- NFOCConnectionGraph.connectComponents: should remove equations generated from: connect(" +
742 ComponentRef.toString(edge.lhs) + ", " +
743 ComponentRef.toString(edge.rhs) + ") and add {0, ..., 0} = equalityConstraint(cr1, cr2) instead.\n");
744 end if;
745
746 // break the connect(ref1, ref2)
747 outConnectedConnections := {};
748 32 eq := generateEqualityConstraintEquation(edge.lhs, edge.rhs, edge.source);
749 32 outBrokenConnections := {FlatEdge.BROKEN_EDGE(edge.lhs, edge.rhs, edge.source, {eq})};
750 else
751 // leave the connect(ref1,ref2)
752 outConnectedConnections := {edge};
753 outBrokenConnections := {};
754 end try;
755 end connectComponents;
756
757 protected function connectCanonicalComponents
758 "Tries to connect two components whose canonical elements are given.
759 Helper function for connectionComponents."
760 input CrefCrefTable inPartition;
761 input ComponentRef inRef1;
762 input ComponentRef inRef2;
763 output Boolean outReallyConnected;
764 algorithm
765 2859 outReallyConnected := not ComponentRef.isEqual(inRef1, inRef2);
766
767
2/2
✓ Branch 0 taken 230 times.
✓ Branch 1 taken 2629 times.
2859 if outReallyConnected then
768 2629 UnorderedMap.add(inRef1, inRef2, inPartition);
769 end if;
770 end connectCanonicalComponents;
771
772 protected function addRootsToTable
773 "Adds a root the the graph. This is implemented by connecting the root to inFirstRoot element."
774 input CrefCrefTable table;
775 input list<ComponentRef> roots;
776 input ComponentRef firstRoot;
777 protected
778 ComponentRef root;
779 algorithm
780
2/2
✓ Branch 0 taken 131 times.
✓ Branch 1 taken 76 times.
207 for root in roots loop
781 131 UnorderedMap.add(root, firstRoot, table);
782 end for;
783 end addRootsToTable;
784
785 protected function resultGraphWithRoots
786 "Creates an initial graph with given definite roots."
787 input list<ComponentRef> roots;
788 output CrefCrefTable outTable;
789 protected
790 ComponentRef dummyRoot;
791 algorithm
792 228 dummyRoot := NFBuiltin.TIME_CREF;
793 76 outTable := newCrefCrefTable();
794 76 addRootsToTable(outTable, roots, dummyRoot);
795 end resultGraphWithRoots;
796
797 protected function addBranchesToTable
798 "Adds all branches to the graph."
799 input CrefCrefTable table;
800 input Edges branches;
801 protected
802 ComponentRef ref1, ref2;
803 algorithm
804
2/2
✓ Branch 0 taken 620 times.
✓ Branch 1 taken 76 times.
696 for branch in branches loop
805 620 (ref1, ref2) := branch;
806 620 connectBranchComponents(table, ref1, ref2);
807 end for;
808 end addBranchesToTable;
809
810 protected function ord
811 "An ordering function for potential roots."
812 input PotentialRoot inEl1;
813 input PotentialRoot inEl2;
814 output Boolean outBoolean;
815 algorithm
816 outBoolean := matchcontinue(inEl1, inEl2)
817 local
818 Real r1, r2;
819 ComponentRef c1, c2;
820 String s1, s2;
821
822 case((c1,r1), (c2,r2)) // if equal order by cref
823 algorithm
824
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 326 times.
332 true := realEq(r1, r2);
825 326 s1 := ComponentRef.toString(c1);
826 326 s2 := ComponentRef.toString(c2);
827
2/2
✓ Branch 1 taken 227 times.
✓ Branch 2 taken 99 times.
326 1 := stringCompare(s1, s2);
828 then
829 true;
830
831 case((_,r1), (_,r2))
832 233 then r1 > r2;
833 end matchcontinue;
834 end ord;
835
836 protected function addPotentialRootsToTable
837 "Adds all potential roots to graph."
838 input CrefCrefTable table;
839 input PotentialRoots potentialRoots;
840 input DefiniteRoots roots;
841 input ComponentRef firstRoot;
842 output DefiniteRoots outRoots;
843 algorithm
844 outRoots := matchcontinue potentialRoots
845 local
846 ComponentRef potentialRoot, canon1, canon2;
847 DefiniteRoots finalRoots;
848 PotentialRoots tail;
849
850 case {} then roots;
851 case (potentialRoot,_)::tail
852 algorithm
853 224 canon1 := canonical(table, potentialRoot);
854 224 canon2 := canonical(table, firstRoot);
855
2/2
✓ Branch 1 taken 198 times.
✓ Branch 2 taken 26 times.
224 true := connectCanonicalComponents(table, canon1, canon2);
856 26 finalRoots := addPotentialRootsToTable(table, tail, potentialRoot::roots, firstRoot);
857 then finalRoots;
858 case _::tail
859 algorithm
860 198 finalRoots := addPotentialRootsToTable(table, tail, roots, firstRoot);
861 then finalRoots;
862 end matchcontinue;
863 end addPotentialRootsToTable;
864
865 protected function addConnections
866 "Adds all connections to graph."
867 input CrefCrefTable table;
868 input FlatEdges inConnections;
869 output FlatEdges outConnectedConnections = {};
870 output FlatEdges outBrokenConnections = {};
871 protected
872 FlatEdges connected, broken;
873 algorithm
874
2/2
✓ Branch 0 taken 2015 times.
✓ Branch 1 taken 76 times.
2091 for c in inConnections loop
875 2015 (connected, broken) := connectComponents(table, c);
876 2015 outConnectedConnections := listAppend(connected, outConnectedConnections);
877 2015 outBrokenConnections := listAppend(broken, outBrokenConnections);
878 end for;
879 end addConnections;
880
881 protected function findResultGraph
882 "Given NFOCConnectionGraph structure, breaks all connections,
883 determines roots and generates a list of dae elements."
884 input NFOCConnectionGraph inGraph;
885 input String modelNameQualified;
886 output DefiniteRoots outRoots;
887 output FlatEdges outConnectedConnections;
888 output FlatEdges outBrokenConnections;
889 algorithm
890 (outRoots, outConnectedConnections, outBrokenConnections) := match inGraph
891 local
892 DefiniteRoots definiteRoots, finalRoots;
893 PotentialRoots potentialRoots, orderedPotentialRoots;
894 UniqueRoots uniqueRoots;
895 Edges branches;
896 FlatEdges connections, broken, connected;
897 CrefCrefTable table;
898 ComponentRef dummyRoot;
899 String brokenConnectsViaGraphViz;
900 list<String> userBrokenLst;
901 list<list<String>> userBrokenLstLst;
902 list<tuple<String,String>> userBrokenTplLst;
903
904 // deal with empty connection graph
905 case GRAPH(definiteRoots = {}, potentialRoots = {}, uniqueRoots = {}, branches = {}, connections = {})
906 then ({}, {}, {});
907
908 // we have something in the connection graph
909 case GRAPH(definiteRoots = definiteRoots, potentialRoots = potentialRoots, uniqueRoots = uniqueRoots,
910 branches = branches, connections = connections)
911 algorithm
912 // reverse the conenction list to have them as in the model
913 76 connections := listReverse(connections);
914 // add definite roots to the table
915 76 table := resultGraphWithRoots(definiteRoots);
916 // add branches to the table
917 76 addBranchesToTable(table, branches);
918 // order potential roots in the order or priority
919 76 orderedPotentialRoots := List.sort(potentialRoots, ord);
920
921
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 76 times.
76 if Flags.isSet(Flags.CGRAPH) then
922 ✗ print("Ordered Potential Roots: " + stringDelimitList(List.map(orderedPotentialRoots, printPotentialRootTuple), ", ") + "\n");
923 end if;
924
925 // add connections to the table and return the broken/connected connections
926 76 (connected, broken) := addConnections(table, connections);
927
928 // create a dummy root
929 228 dummyRoot := NFBuiltin.TIME_CREF;
930 // select final roots
931 76 finalRoots := addPotentialRootsToTable(table, orderedPotentialRoots, definiteRoots, dummyRoot);
932
933 // generate the graphviz representation and display
934 76 brokenConnectsViaGraphViz := generateGraphViz(modelNameQualified, definiteRoots, potentialRoots, uniqueRoots, branches, connections, finalRoots, broken);
935
936
2/4
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 76 times.
76 if stringEq(brokenConnectsViaGraphViz, "")
937 then
938 // if brokenConnectsViaGraphViz is empty, the user wants to use the current breaking!
939 else
940 // interpret brokenConnectsViaGraphViz and pass it to the breaking algorithm again
941 // graphviz returns the broken connects as: cr1|cr2#cr3|cr4#
942 ✗ userBrokenLst := Util.stringSplitAtChar(brokenConnectsViaGraphViz, "#");
943 ✗ userBrokenLstLst := List.map1(userBrokenLst, Util.stringSplitAtChar, "|");
944 ✗ userBrokenTplLst := makeTuple(userBrokenLstLst);
945 ✗ print("User selected the following connect edges for breaking:\n\t" + stringDelimitList(List.map(userBrokenTplLst, printTupleStr), "\n\t") + "\n");
946 // print("\nBefore ordering:\n");
947 ✗ printFlatEdges(connections);
948 // order the connects with the input given by the user!
949 ✗ connections := orderConnectsGuidedByUser(connections, userBrokenTplLst);
950 // reverse the reverse! uh oh!
951 ✗ connections := listReverse(connections);
952 ✗ print("\nAfer ordering:\n");
953 // printFlatEdges(connections);
954 // call findResultGraph again with ordered connects!
955 ✗ (finalRoots, connected, broken) :=
956 findResultGraph(GRAPH(false, definiteRoots, potentialRoots, uniqueRoots, branches, connections), modelNameQualified);
957 end if;
958
959 76 then
960 (finalRoots, connected, broken);
961
962 end match;
963 end findResultGraph;
964
965 protected function orderConnectsGuidedByUser
966 input FlatEdges inConnections;
967 input list<tuple<String,String>> inUserSelectedBreaking;
968 output FlatEdges outOrderedConnections;
969 protected
970 FlatEdges front = {};
971 FlatEdges back = {};
972 String sc1,sc2;
973 algorithm
974 ✗ for e in inConnections loop
975 ✗ sc1 := ComponentRef.toString(e.lhs);
976 ✗ sc2 := ComponentRef.toString(e.rhs);
977
978 ✗ if listMember((sc1, sc2), inUserSelectedBreaking) or listMember((sc2, sc1), inUserSelectedBreaking) then
979 // put them at the end to be tried last (more chance to be broken)
980 ✗ back := e::back;
981 else
982 // put them at the front to be tried first (less chance to be broken)
983 front := e::front;
984 end if;
985 end for;
986 ✗ outOrderedConnections := List.append_reverse(front, back);
987 end orderConnectsGuidedByUser;
988
989 protected function printTupleStr
990 input tuple<String,String> inTpl;
991 output String out;
992 algorithm
993 out := match inTpl
994 local
995 String c1,c2;
996 ✗ case (c1,c2) then c1 + " -- " + c2;
997 end match;
998 end printTupleStr;
999
1000 protected function makeTuple
1001 input list<list<String>> inLstLst;
1002 output list<tuple<String,String>> outLst;
1003 algorithm
1004 outLst := matchcontinue inLstLst
1005 local
1006 String c1,c2;
1007 list<list<String>> rest;
1008 list<tuple<String,String>> lst;
1009 list<String> bad;
1010
1011 // empty case
1012 case {} then {};
1013 // somthing case
1014 case {c1,c2}::rest
1015 algorithm
1016 ✗ lst := makeTuple(rest);
1017 ✗ then
1018 (c1,c2)::lst;
1019 // ignore empty strings
1020 case {""}::rest
1021 algorithm
1022 ✗ lst := makeTuple(rest);
1023 then
1024 lst;
1025 // ignore empty list
1026 case {}::rest
1027 algorithm
1028 ✗ lst := makeTuple(rest);
1029 then
1030 lst;
1031 // somthing case
1032 case bad::rest
1033 algorithm
1034 ✗ print("The following output from GraphViz OpenModelica assistant cannot be parsed:" +
1035 stringDelimitList(bad, ", ") +
1036 "\nExpected format from GrapViz: cref1|cref2#cref3|cref4#. Ignoring malformed input.\n");
1037 ✗ lst := makeTuple(rest);
1038 then
1039 lst;
1040 end matchcontinue;
1041 end makeTuple;
1042
1043 protected function printPotentialRootTuple
1044 input PotentialRoot potentialRoot;
1045 output String outStr;
1046 algorithm
1047 outStr := match potentialRoot
1048 local
1049 ComponentRef cr;
1050 Real priority;
1051 String str;
1052 case (cr, priority)
1053 algorithm
1054 ✗ str := ComponentRef.toString(cr) + "(" + realString(priority) + ")";
1055 then str;
1056 end match;
1057 end printPotentialRootTuple;
1058
1059 protected function buildRootedTable
1060 "Distances to the roots in the spanning tree, i.e. without the broken connections."
1061 input list<ComponentRef> roots;
1062 input FlatEdges connected;
1063 input NFOCConnectionGraph graph;
1064 output CrefIndexTable rooted;
1065 protected
1066 CrefRootsTable table;
1067 algorithm
1068 76 table := UnorderedMap.new<DefiniteRoots>(ComponentRef.hash, ComponentRef.isEqual);
1069
1070 // Add branches and connections to table.
1071 76 List.map1_0(getBranches(graph), addBranches, table);
1072 76 List.map1_0(connected, addConnectionsRooted, table);
1073
1074 // Get distance to root.
1075 76 rooted := UnorderedMap.new<Integer>(ComponentRef.hash, ComponentRef.isEqual);
1076 76 setRootDistance(roots, table, 0, {}, rooted);
1077 end buildRootedTable;
1078
1079 protected function setRootDistance
1080 "Breadth-first walk from the roots."
1081 input list<ComponentRef> finalRoots;
1082 input CrefRootsTable table;
1083 input Integer distance;
1084 input list<ComponentRef> nextLevel;
1085 input CrefIndexTable rooted;
1086 protected
1087 list<ComponentRef> level = finalRoots, next = nextLevel, neighbors;
1088 Integer dist = distance;
1089 ComponentRef cr;
1090 algorithm
1091 while true loop
1092
2/2
✓ Branch 0 taken 1062 times.
✓ Branch 1 taken 5347 times.
6409 if listEmpty(level) then
1093
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 986 times.
1062 if listEmpty(next) then
1094 76 return;
1095 end if;
1096 level := next;
1097 next := {};
1098 986 dist := dist + 1;
1099 else
1100 5347 cr::level := level;
1101
2/2
✓ Branch 1 taken 2595 times.
✓ Branch 2 taken 2752 times.
5347 if not UnorderedMap.contains(cr, rooted) then
1102 2752 UnorderedMap.addNew(cr, dist, rooted);
1103 next := match UnorderedMap.get(cr, table)
1104 2732 case SOME(neighbors) then listAppend(next, neighbors);
1105 else next;
1106 end match;
1107 end if;
1108 end if;
1109 end while;
1110 end setRootDistance;
1111
1112 protected function addBranches
1113 input Edge edge;
1114 input CrefRootsTable table;
1115 protected
1116 ComponentRef cref1,cref2;
1117 algorithm
1118 620 (cref1,cref2) := edge;
1119 620 addConnectionRooted(cref1,cref2,table);
1120 620 addConnectionRooted(cref2,cref1,table);
1121 end addBranches;
1122
1123 protected function addConnectionsRooted
1124 input FlatEdge connection;
1125 input CrefRootsTable table;
1126 algorithm
1127 1983 addConnectionRooted(connection.lhs,connection.rhs,table);
1128 1983 addConnectionRooted(connection.rhs,connection.lhs,table);
1129 end addConnectionsRooted;
1130
1131 protected function addConnectionRooted
1132 input ComponentRef cref1;
1133 input ComponentRef cref2;
1134 input CrefRootsTable table;
1135
1136 function updateRooted
1137 input Option<DefiniteRoots> roots;
1138 input ComponentRef newRoot;
1139 output DefiniteRoots outRoots;
1140 algorithm
1141 outRoots := match roots
1142 case SOME(outRoots) then newRoot :: outRoots;
1143 else {newRoot};
1144 end match;
1145 end updateRooted;
1146 algorithm
1147 5206 UnorderedMap.addUpdate(cref1, function updateRooted(newRoot = cref2), table);
1148 end addConnectionRooted;
1149
1150 protected function evalConnectionsOperatorsEqs
1151 input list<ComponentRef> inRoots;
1152 input CrefIndexTable rooted;
1153 input NFOCConnectionGraph graph;
1154 input output list<Equation> equations;
1155 algorithm
1156
4/4
✓ Branch 0 taken 26265 times.
✓ Branch 1 taken 152 times.
✓ Branch 2 taken 26265 times.
✓ Branch 3 taken 152 times.
26417 equations := list(Equation.mapExpShallow(eq,
1157 function evaluateOperators(rooted = rooted, roots = inRoots, graph = graph, info = Equation.info(eq)))
1158 for eq in equations);
1159 end evalConnectionsOperatorsEqs;
1160
1161 protected function evalConnectionsOperatorsVar
1162 input list<ComponentRef> roots;
1163 input CrefIndexTable rooted;
1164 input NFOCConnectionGraph graph;
1165 input output Variable var;
1166 algorithm
1167 142868 var.binding := Binding.mapExpShallow(var.binding,
1168 function evaluateOperators(rooted = rooted, roots = roots, graph = graph, info = var.info));
1169 end evalConnectionsOperatorsVar;
1170
1171 function evaluateOperators
1172 "evaluation of Connections.rooted, Connections.isRoot, Connections.uniqueRootIndices
1173 - replaces all [Connections.]rooted calls by true or false depending on wheter branche frame_a or frame_b is closer to root
1174 - return true or false for Connections.isRoot operator if is a root or not
1175 - return an array of indices for Connections.uniqueRootIndices, see Modelica_StateGraph2
1176 See Modelica_StateGraph2:
1177 https://github.com/modelica/Modelica_StateGraph2 and
1178 https://trac.modelica.org/Modelica/ticket/984 and
1179 http://www.ep.liu.se/ecp/043/041/ecp09430108.pdf
1180 for a specification of this operator"
1181 input output Expression exp;
1182 input CrefIndexTable rooted;
1183 input list<ComponentRef> roots;
1184 input NFOCConnectionGraph graph;
1185 input SourceInfo info;
1186 algorithm
1187 88114 exp := Expression.map(exp,
1188 function evalConnectionsOperatorsHelper(rooted = rooted, roots = roots, graph = graph, info = info));
1189 end evaluateOperators;
1190
1191 protected function evalConnectionsOperatorsHelper
1192 "Helper function for evaluation of Connections.rooted, Connections.isRoot, Connections.uniqueRootIndices"
1193 input Expression exp;
1194 input CrefIndexTable rooted;
1195 input list<ComponentRef> roots;
1196 input NFOCConnectionGraph graph;
1197 input SourceInfo info;
1198 output Expression outExp;
1199 algorithm
1200 outExp := match exp
1201 local
1202 Expression uroots, nodes, message, res;
1203 ComponentRef cref;
1204 Boolean result;
1205 list<Boolean> results;
1206 Edges branches;
1207 Call call;
1208 String str;
1209 Dimension dim;
1210
1211 case Expression.CALL(call = call as Call.TYPED_CALL())
1212 then match identifyConnectionsOperator(Function.name(call.fn))
1213 // handle rooted - with zero size array or the normal call
1214 case ConnectionsOperator.ROOTED
1215 algorithm
1216 res := match call.arguments
1217 // zero size array TODO! FIXME! check how zero size arrays are handled in the NF
1218 case _ guard Expression.isEmptyArray(listHead(call.arguments))
1219 algorithm
1220 ✗ if Flags.isSet(Flags.CGRAPH) then
1221 ✗ print("- NFOCConnectionGraph.evalConnectionsOperatorsHelper: " + Expression.toString(exp) + " = false\n");
1222 end if;
1223 then
1224 Expression.BOOLEAN(false);
1225
1226 // normal call
1227 case {Expression.CREF(cref = cref)}
1228 algorithm
1229 // find partner in branches
1230 139 branches := getBranches(graph);
1231 139 cref := ComponentRef.stripIteratorSubscripts(cref);
1232
1233 try
1234 139 result := elementRooted(cref, branches, rooted);
1235 else
1236 // an array of components in a for-equation, e.g. rooted(joint[$i].frame_a.R):
1237 // the graph has its elements, which all have to agree
1238 try
1239 ✗ result :: results := list(elementRooted(c, branches, rooted) for c in componentElements(cref));
1240 ✗ true := List.all(results, function boolEq(b2 = result));
1241 else
1242 ✗ str := ComponentRef.toString(cref);
1243 ✗ Error.addSourceMessage(Error.OCG_MISSING_BRANCH, {str, str, str}, info);
1244 result := false;
1245 end try;
1246 end try;
1247
1248
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 139 times.
139 if Flags.isSet(Flags.CGRAPH) then
1249 ✗ print("- NFOCConnectionGraph.evalConnectionsOperatorsHelper: " + Expression.toString(exp) + " = " + boolString(result) + "\n");
1250 end if;
1251
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 136 times.
142 then
1252 Expression.BOOLEAN(result);
1253 end match;
1254 then
1255 res;
1256
1257 // deal with Connections.isRoot - with zero size array and normal
1258 case ConnectionsOperator.IS_ROOT
1259 algorithm
1260 res := match call.arguments
1261 // zero size array TODO! FIXME! check how zero size arrays are handled in the NF
1262 case _ guard Expression.isEmptyArray(listHead(call.arguments))
1263 algorithm
1264 ✗ if Flags.isSet(Flags.CGRAPH) then
1265 ✗ print("- NFOCConnectionGraph.evalConnectionsOperatorsHelper: " + Expression.toString(exp) + " = false\n");
1266 end if;
1267 then
1268 Expression.BOOLEAN(false);
1269
1270 // normal call
1271 case {Expression.CREF(cref = cref)}
1272 algorithm
1273 236 cref := ComponentRef.stripIteratorSubscripts(cref);
1274 236 result := List.isMemberOnTrue(cref, roots, ComponentRef.isEqual);
1275
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 236 times.
236 if Flags.isSet(Flags.CGRAPH) then
1276 ✗ print("- NFOCConnectionGraph.evalConnectionsOperatorsHelper: " + Expression.toString(exp) + " = " + boolString(result) + "\n");
1277 end if;
1278
2/2
✓ Branch 0 taken 200 times.
✓ Branch 1 taken 36 times.
436 then
1279 Expression.BOOLEAN(result);
1280 end match;
1281 then
1282 res;
1283
1284 // deal with Connections.uniqueRootIndices, TODO! FIXME! actually implement this
1285 case ConnectionsOperator.UNIQUE_ROOT_INDICES
1286 algorithm
1287 res := match call.arguments
1288 // normal call
1289 case {uroots,nodes,message}
1290 algorithm
1291 ✗ if Flags.isSet(Flags.CGRAPH) then
1292 ✗ print("- NFOCConnectionGraph.evalConnectionsOperatorsHelper: Connections.uniqueRootsIndices(" +
1293 Expression.toString(uroots) + "," +
1294 Expression.toString(nodes) + "," +
1295 Expression.toString(message) + ")\n");
1296 end if;
1297
1298 ✗ dim := Type.nthDimension(Expression.typeOf(uroots), 1);
1299
1300 ✗ if not Dimension.isKnown(dim) then
1301 ✗ Error.addSourceMessage(Error.DIMENSION_NOT_KNOWN,
1302 {Expression.toString(exp)}, info);
1303 ✗ fail();
1304 end if;
1305 ✗ then
1306 Expression.fillArray(Dimension.size(dim), Expression.INTEGER(1)); // TODO! FIXME! actually implement this correctly
1307 end match;
1308 then
1309 res;
1310
1311 else exp;
1312 end match;
1313
1314 // no replacement needed
1315 else exp;
1316 end match;
1317 end evalConnectionsOperatorsHelper;
1318
1319 protected function elementRooted
1320 "Connections.rooted of a connector, fails if it is not part of a branch."
1321 input ComponentRef cref;
1322 input Edges branches;
1323 input CrefIndexTable rooted;
1324 output Boolean result = getRooted(cref, getEdge(cref, branches), rooted);
1325 end elementRooted;
1326
1327 protected function getRooted
1328 input ComponentRef cref1;
1329 input ComponentRef cref2;
1330 input CrefIndexTable rooted;
1331 output Boolean result;
1332 algorithm
1333 result := matchcontinue rooted
1334 local
1335 Integer i1,i2;
1336 case _
1337 algorithm
1338 139 i1 := UnorderedMap.getOrFail(cref1,rooted);
1339 139 i2 := UnorderedMap.getOrFail(cref2,rooted);
1340 139 then
1341 intLt(i1,i2);
1342 // in fail case return true
1343 else true;
1344 end matchcontinue;
1345 end getRooted;
1346
1347 protected function getEdge
1348 "return the Edge partner of a edge, fails if not found"
1349 input ComponentRef cr;
1350 input Edges edges;
1351 output ComponentRef ocr;
1352 protected
1353 ComponentRef cref1, cref2;
1354 algorithm
1355
1/2
✓ Branch 0 taken 2257 times.
✗ Branch 1 not taken.
2257 for edge in edges loop
1356 2257 (cref1, cref2) := edge;
1357
1358
2/2
✓ Branch 1 taken 139 times.
✓ Branch 2 taken 2118 times.
2257 if ComponentRef.isEqual(cr, cref1) then
1359 ocr := cref2;
1360 139 return;
1361 elseif ComponentRef.isEqual(cr, cref2) then
1362 ocr := cref1;
1363 ✗ return;
1364 end if;
1365 end for;
1366
1367 ✗ fail();
1368 end getEdge;
1369
1370 protected function printConnectionStr
1371 "prints the connection str"
1372 input FlatEdge edge;
1373 input String ty;
1374 output String outStr;
1375 algorithm
1376 ✗ outStr := ty + "(" + ComponentRef.toString(edge.lhs) + ", " + ComponentRef.toString(edge.rhs) + ")";
1377 end printConnectionStr;
1378
1379 protected function printEdges
1380 "Prints a list of edges to stdout."
1381 input Edges inEdges;
1382 algorithm
1383 () := match inEdges
1384 local
1385 ComponentRef c1, c2;
1386 Edges tail;
1387
1388 case {} then ();
1389 case (c1, c2) :: tail
1390 algorithm
1391 ✗ print(" ");
1392 ✗ print(ComponentRef.toString(c1));
1393 ✗ print(" -- ");
1394 ✗ print(ComponentRef.toString(c2));
1395 ✗ print("\n");
1396 ✗ printEdges(tail);
1397 then ();
1398 end match;
1399 end printEdges;
1400
1401 protected function printFlatEdges
1402 "Prints a list of dae edges to stdout."
1403 input FlatEdges inEdges;
1404 algorithm
1405 ✗ for edge in inEdges loop
1406 ✗ print(" ");
1407 ✗ print(ComponentRef.toString(edge.lhs));
1408 ✗ print(" -- ");
1409 ✗ print(ComponentRef.toString(edge.rhs));
1410 ✗ print("\n");
1411 end for;
1412 end printFlatEdges;
1413
1414 protected function printNFOCConnectionGraph
1415 "Prints the content of NFOCConnectionGraph structure."
1416 input NFOCConnectionGraph inGraph;
1417 algorithm
1418 () := match inGraph
1419 local
1420 FlatEdges connections;
1421 Edges branches;
1422
1423 case GRAPH(connections = connections, branches = branches)
1424 algorithm
1425 ✗ print("Connections:\n");
1426 ✗ printFlatEdges(connections);
1427 ✗ print("Branches:\n");
1428 ✗ printEdges(branches);
1429 then ();
1430 end match;
1431 end printNFOCConnectionGraph;
1432
1433 protected function getDefiniteRoots
1434 "Accessor for NFOCConnectionGraph.definiteRoots."
1435 input NFOCConnectionGraph inGraph;
1436 output DefiniteRoots outResult;
1437 algorithm
1438 outResult := match inGraph
1439 local
1440 DefiniteRoots result;
1441 case GRAPH(definiteRoots = result) then result;
1442 end match;
1443 end getDefiniteRoots;
1444
1445 protected function getUniqueRoots
1446 "Accessor for NFOCConnectionGraph.uniqueRoots."
1447 input NFOCConnectionGraph inGraph;
1448 output UniqueRoots outResult;
1449 algorithm
1450 outResult := match inGraph
1451 local
1452 UniqueRoots result;
1453 case GRAPH(uniqueRoots = result) then result;
1454 end match;
1455 end getUniqueRoots;
1456
1457 protected function getPotentialRoots
1458 "Accessor for NFOCConnectionGraph.potentialRoots."
1459 input NFOCConnectionGraph inGraph;
1460 output PotentialRoots outResult;
1461 algorithm
1462 outResult := match inGraph
1463 local PotentialRoots result;
1464 case GRAPH(potentialRoots = result) then result;
1465 end match;
1466 end getPotentialRoots;
1467
1468 protected function getBranches
1469 "Accessor for NFOCConnectionGraph.branches."
1470 input NFOCConnectionGraph inGraph;
1471 output Edges outResult;
1472 algorithm
1473 outResult := match inGraph
1474 local Edges result;
1475 case GRAPH(branches = result) then result;
1476 end match;
1477 end getBranches;
1478
1479 protected function getConnections
1480 "Accessor for NFOCConnectionGraph.connections."
1481 input NFOCConnectionGraph inGraph;
1482 output FlatEdges outResult;
1483 algorithm
1484 outResult := match inGraph
1485 local FlatEdges result;
1486 case GRAPH(connections = result) then result;
1487 end match;
1488 end getConnections;
1489
1490 function merge
1491 "merge two NFOCConnectionGraphs"
1492 input NFOCConnectionGraph inGraph1;
1493 input NFOCConnectionGraph inGraph2;
1494 output NFOCConnectionGraph outGraph;
1495 algorithm
1496 outGraph := match(inGraph1, inGraph2)
1497 local
1498 Boolean updateGraph, updateGraph1, updateGraph2;
1499 DefiniteRoots definiteRoots, definiteRoots1, definiteRoots2;
1500 UniqueRoots uniqueRoots, uniqueRoots1, uniqueRoots2;
1501 PotentialRoots potentialRoots, potentialRoots1, potentialRoots2;
1502 Edges branches, branches1, branches2;
1503 FlatEdges connections, connections1, connections2;
1504
1505 // left is empty, return right
1506 case (_, GRAPH(definiteRoots = {},potentialRoots = {},uniqueRoots = {},branches = {},connections = {}))
1507 then
1508 inGraph1;
1509
1510 // right is empty, return left
1511 case (GRAPH(definiteRoots = {},potentialRoots = {},uniqueRoots = {},branches = {},connections = {}), _)
1512 then
1513 inGraph2;
1514
1515 // they are equal, return any
1516 case (_, _) guard valueEq(inGraph1, inGraph2)
1517 then
1518 inGraph1;
1519
1520 // they are NOT equal, merge them
1521 case (GRAPH(updateGraph = updateGraph1, definiteRoots = definiteRoots1, potentialRoots = potentialRoots1, uniqueRoots=uniqueRoots1,
1522 branches = branches1, connections = connections1),
1523 GRAPH(updateGraph = updateGraph2, definiteRoots = definiteRoots2, potentialRoots = potentialRoots2, uniqueRoots=uniqueRoots2,
1524 branches = branches2,connections = connections2))
1525 algorithm
1526 ✗ if Flags.isSet(Flags.CGRAPH) then
1527 ✗ Debug.trace("- NFOCConnectionGraph.merge()\n");
1528 end if;
1529 ✗ updateGraph := boolOr(updateGraph1, updateGraph2);
1530 ✗ definiteRoots := List.union(definiteRoots1, definiteRoots2);
1531 ✗ potentialRoots := List.union(potentialRoots1, potentialRoots2);
1532 ✗ uniqueRoots := List.union(uniqueRoots1, uniqueRoots2);
1533 ✗ branches := List.union(branches1, branches2);
1534 ✗ connections := List.union(connections1, connections2);
1535 ✗ then
1536 GRAPH(updateGraph,definiteRoots,potentialRoots,uniqueRoots,branches,connections);
1537 end match;
1538 end merge;
1539
1540 /***********************************************************************************************************************/
1541 /******************************************* GraphViz generation *******************************************************/
1542 /***********************************************************************************************************************/
1543
1544 protected function graphVizEdge
1545 input Edge inEdge;
1546 output String out;
1547 algorithm
1548 out := match inEdge
1549 local ComponentRef c1, c2; String strEdge;
1550 case (c1, c2)
1551 algorithm
1552 ✗ strEdge := "\"" + ComponentRef.toString(c1) + "\" -- \"" + ComponentRef.toString(c2) + "\"" +
1553 " [color = blue, dir = \"none\", fontcolor=blue, label = \"branch\"];\n\t";
1554 then strEdge;
1555 end match;
1556 end graphVizEdge;
1557
1558 protected function graphVizFlatEdge
1559 input FlatEdge edge;
1560 input FlatEdges inBrokenFlatEdges;
1561 output String out;
1562 protected
1563 String sc1, sc2, label, labelFontSize, decorate, color, style, fontColor;
1564 Boolean isBroken;
1565 algorithm
1566 ✗ isBroken := List.isMemberOnTrue(edge, inBrokenFlatEdges, FlatEdgeIsEqual);
1567 ✗ label := if isBroken then "[[broken connect]]" else "connect";
1568 ✗ color := if isBroken then "red" else "green";
1569 ✗ style := if isBroken then "\"bold, dashed\"" else "solid";
1570 ✗ decorate := boolString(isBroken);
1571 fontColor := if isBroken then "red" else "green";
1572 ✗ labelFontSize := if isBroken then "labelfontsize = 20.0, " else "";
1573 ✗ sc1 := ComponentRef.toString(edge.lhs);
1574 ✗ sc2 := ComponentRef.toString(edge.rhs);
1575 ✗ out := stringAppendList({
1576 "\"", sc1, "\" -- \"", sc2, "\" [",
1577 "dir = \"none\", ",
1578 "style = ", style, ", ",
1579 "decorate = ", decorate, ", ",
1580 "color = ", color , ", ",
1581 labelFontSize,
1582 "fontcolor = ", fontColor , ", ",
1583 "label = \"", label ,"\"",
1584 "];\n\t"});
1585 end graphVizFlatEdge;
1586
1587 protected function FlatEdgeIsEqual
1588 input FlatEdge inEdge1;
1589 input FlatEdge inEdge2;
1590 output Boolean isEqual;
1591 algorithm
1592 ✗ isEqual := ComponentRef.isEqual(inEdge1.lhs, inEdge2.lhs) and
1593 ComponentRef.isEqual(inEdge1.rhs, inEdge2.rhs);
1594 end FlatEdgeIsEqual;
1595
1596 protected function graphVizDefiniteRoot
1597 input DefiniteRoot inDefiniteRoot;
1598 input DefiniteRoots inFinalRoots;
1599 output String out;
1600 algorithm
1601 out := match inDefiniteRoot
1602 local ComponentRef c; String strDefiniteRoot; Boolean isSelectedRoot;
1603 case c
1604 algorithm
1605 ✗ isSelectedRoot := List.isMemberOnTrue(c, inFinalRoots, ComponentRef.isEqual);
1606 ✗ strDefiniteRoot := "\"" + ComponentRef.toString(c) + "\"" +
1607 " [fillcolor = red, rank = \"source\", label = " + "\"" + ComponentRef.toString(c) + "\", " +
1608 (if isSelectedRoot then "shape=polygon, sides=8, distortion=\"0.265084\", orientation=26, skew=\"0.403659\"" else "shape=box") +
1609 "];\n\t";
1610 then strDefiniteRoot;
1611 end match;
1612 end graphVizDefiniteRoot;
1613
1614 protected function graphVizPotentialRoot
1615 input PotentialRoot inPotentialRoot;
1616 input DefiniteRoots inFinalRoots;
1617 output String out;
1618 algorithm
1619 out := match inPotentialRoot
1620 local ComponentRef c; Real priority; String strPotentialRoot; Boolean isSelectedRoot;
1621 case (c, priority)
1622 algorithm
1623 ✗ isSelectedRoot := List.isMemberOnTrue(c, inFinalRoots, ComponentRef.isEqual);
1624 ✗ strPotentialRoot := "\"" + ComponentRef.toString(c) + "\"" +
1625 " [fillcolor = orangered, rank = \"min\" label = " + "\"" + ComponentRef.toString(c) + "\\n" + realString(priority) + "\", " +
1626 (if isSelectedRoot then "shape=ploygon, sides=7, distortion=\"0.265084\", orientation=26, skew=\"0.403659\"" else "shape=box") +
1627 "];\n\t";
1628 then strPotentialRoot;
1629 end match;
1630 end graphVizPotentialRoot;
1631
1632 protected function generateGraphViz
1633 "@author: adrpo
1634 Generate a graphviz file out of the connection graph"
1635 input String modelNameQualified;
1636 input DefiniteRoots definiteRoots;
1637 input PotentialRoots potentialRoots;
1638 input UniqueRoots uniqueRoots;
1639 input Edges branches;
1640 input FlatEdges connections;
1641 input DefiniteRoots finalRoots;
1642 input FlatEdges broken;
1643 output String brokenConnectsViaGraphViz;
1644 algorithm
1645 brokenConnectsViaGraphViz := matchcontinue broken
1646 local
1647 String fileName, i, nrDR, nrPR, nrUR, nrBR, nrCO, nrFR, nrBC, timeStr, infoNodeStr, brokenConnects;
1648 Real tStart, tEnd, t;
1649 IOStream.IOStream graphVizStream;
1650 list<String> infoNode;
1651
1652 // don't do anything if we don't have -d=cgraphGraphVizFile or -d=cgraphGraphVizShow
1653 case _
1654 algorithm
1655
2/4
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 76 times.
76 false := boolOr(Flags.isSet(Flags.CGRAPH_GRAPHVIZ_FILE), Flags.isSet(Flags.CGRAPH_GRAPHVIZ_SHOW));
1656 then
1657 "";
1658
1659 case _
1660 algorithm
1661 ✗ tStart := clock();
1662 i := "\t";
1663 ✗ fileName := stringAppend(modelNameQualified, ".gv");
1664 // create a stream
1665 ✗ graphVizStream := IOStream.create(fileName, IOStream.LIST());
1666 ✗ nrDR := intString(listLength(definiteRoots));
1667 ✗ nrPR := intString(listLength(potentialRoots));
1668 ✗ nrUR := intString(listLength(uniqueRoots));
1669 ✗ nrBR := intString(listLength(branches));
1670 ✗ nrCO := intString(listLength(connections));
1671 ✗ nrFR := intString(listLength(finalRoots));
1672 ✗ nrBC := intString(listLength(broken));
1673
1674 infoNode :=
1675 {
1676 "// Generated by OpenModelica.\n",
1677 "// Overconstrained connection graph for model:\n// ", modelNameQualified, "\n",
1678 "//\n",
1679 "// Summary:\n",
1680 "// Roots: ", nrDR, "\n",
1681 "// Potential Roots: ", nrPR, "\n",
1682 "// Unique Roots: ", nrUR, "\n",
1683 "// Branches: ", nrBR, "\n",
1684 "// Connections: ", nrCO, "\n",
1685 "// Final Roots: ", nrFR, "\n",
1686 "// Broken Connections: ", nrBC, "\n"
1687 };
1688 ✗ infoNodeStr := stringAppendList(infoNode);
1689 // replace \n with \\l (left align), replace \t with " "
1690 ✗ infoNodeStr := System.stringReplace(infoNodeStr, "\n", "\\l"); infoNodeStr := System.stringReplace(infoNodeStr, "\t", " ");
1691 // replace / with ""
1692 ✗ infoNodeStr := System.stringReplace(infoNodeStr, "/", "");
1693
1694 // output header
1695 ✗ graphVizStream := IOStream.appendList(graphVizStream,infoNode);
1696 // output command to be used
1697 // output graphviz header
1698 ✗ graphVizStream := IOStream.appendList(graphVizStream,{"\n\n"});
1699 ✗ graphVizStream := IOStream.appendList(graphVizStream, {"graph \"", modelNameQualified, "\"\n{\n\n"});
1700
1701 // output global settings
1702 ✗ graphVizStream := IOStream.appendList(graphVizStream, {i, "overlap=false;\n"});
1703 ✗ graphVizStream := IOStream.appendList(graphVizStream, {i, "layout=dot;\n\n"});
1704
1705 // output settings for nodes
1706 ✗ graphVizStream := IOStream.appendList(graphVizStream, {i, "node [",
1707 "fillcolor = \"lightsteelblue1\", ",
1708 "shape = box, ",
1709 "style = \"bold, filled\", ",
1710 "rank = \"max\"","]\n\n"});
1711 // output settings for edges
1712 ✗ graphVizStream := IOStream.appendList(graphVizStream, {i, "edge [",
1713 "color = \"black\", ",
1714 "style = bold",
1715 "]\n\n"});
1716
1717 // output summary node
1718 ✗ graphVizStream := IOStream.appendList(graphVizStream, {i, "graph [fontsize=20, fontname = \"Courier Bold\" label= \"\\n\\n", infoNodeStr, "\", size=\"6,6\"];\n", i});
1719
1720 // output definite roots
1721 ✗ graphVizStream := IOStream.appendList(graphVizStream, {"\n", i, "// Definite Roots (Connections.root)", "\n", i});
1722 ✗ graphVizStream := IOStream.appendList(graphVizStream, List.map1(definiteRoots, graphVizDefiniteRoot, finalRoots));
1723 // output potential roots
1724 ✗ graphVizStream := IOStream.appendList(graphVizStream, {"\n", i, "// Potential Roots (Connections.potentialRoot)", "\n", i});
1725 ✗ graphVizStream := IOStream.appendList(graphVizStream, List.map1(potentialRoots, graphVizPotentialRoot, finalRoots));
1726
1727 // output branches
1728 ✗ graphVizStream := IOStream.appendList(graphVizStream, {"\n", i, "// Branches (Connections.branch)", "\n", i});
1729 ✗ graphVizStream := IOStream.appendList(graphVizStream, List.map(branches, graphVizEdge));
1730
1731 // output connections
1732 ✗ graphVizStream := IOStream.appendList(graphVizStream, {"\n", i, "// Connections (connect)", "\n", i});
1733 ✗ graphVizStream := IOStream.appendList(graphVizStream, List.map1(connections, graphVizFlatEdge, broken));
1734
1735 // output graphviz footer
1736 ✗ graphVizStream := IOStream.appendList(graphVizStream, {"\n}\n"});
1737 ✗ tEnd := clock();
1738 ✗ t := tEnd - tStart;
1739 ✗ timeStr := realString(t);
1740 ✗ graphVizStream := IOStream.appendList(graphVizStream, {"\n\n\n// graph generation took: ", timeStr, " seconds\n"});
1741 ✗ System.writeFile(fileName, IOStream.string(graphVizStream));
1742 ✗ print("GraphViz with connection graph for model: " + modelNameQualified + " was writen to file: " + fileName + "\n");
1743 ✗ brokenConnects := showGraphViz(fileName, modelNameQualified);
1744 then
1745 brokenConnects;
1746
1747 end matchcontinue;
1748 end generateGraphViz;
1749
1750 protected function showGraphViz
1751 input String fileNameGraphViz;
1752 input String modelNameQualified;
1753 output String brokenConnectsViaGraphViz;
1754 algorithm
1755 brokenConnectsViaGraphViz := matchcontinue modelNameQualified
1756 local
1757 String leftyCMD, fileNameTraceRemovedConnections, omhome, brokenConnects;
1758 Integer leftyExitStatus;
1759
1760 // do not start graphviz if we don't have -d=cgraphGraphVizShow
1761 case _
1762 algorithm
1763 ✗ false := Flags.isSet(Flags.CGRAPH_GRAPHVIZ_SHOW);
1764 then
1765 "";
1766
1767 else
1768 algorithm
1769 ✗ fileNameTraceRemovedConnections := modelNameQualified + "_removed_connections.txt";
1770 ✗ print("Tyring to start GraphViz *lefty* to visualize the graph. You need to have lefty in your PATH variable\n");
1771 ✗ print("Make sure you quit GraphViz *lefty* via Right Click->quit to be sure the process will be exited.\n");
1772 ✗ print("If you quit the GraphViz *lefty* window via X, please kill the process in task manager to continue.\n");
1773 ✗ omhome := Settings.getInstallationDirectoryPath();
1774 ✗ omhome := System.stringReplace(omhome, "\"", "");
1775 // omhome = System.stringReplace(omhome, "\\", "/");
1776
1777 // create a lefty command and execute it
1778 ✗ leftyCMD := "load('" + omhome + "/share/omc/scripts/openmodelica.lefty');" + "openmodelica.init();openmodelica.createviewandgraph('" +
1779 fileNameGraphViz + "','file',null,null);txtview('off');";
1780 ✗ print("Running command: " + "lefty -e " + leftyCMD + " > " + fileNameTraceRemovedConnections + "\n");
1781 // execute lefty
1782 ✗ leftyExitStatus := System.systemCall("lefty -e " + leftyCMD, fileNameTraceRemovedConnections);
1783 // show the exit status
1784 ✗ print("GraphViz *lefty* exited with status:" + intString(leftyExitStatus) + "\n");
1785 ✗ brokenConnects := System.readFile(fileNameTraceRemovedConnections);
1786 ✗ print("GraphViz OpenModelica assistant returned the following broken connects: " + brokenConnects + "\n");
1787 then
1788 brokenConnects;
1789 end matchcontinue;
1790 end showGraphViz;
1791
1792 function removeBrokenConnects
1793 "@author adrpo:
1794 this function removes the BROKEN connects from the equation list
1795 and keeps the CONNECTED ones."
1796 input list<Equation> inEquations;
1797 input FlatEdges inConnected;
1798 input FlatEdges inBroken;
1799 input IsDeletedFn isDeleted;
1800 output list<Equation> outEquations;
1801 algorithm
1802 outEquations := match inBroken
1803 local
1804 ComponentRef lhs, rhs;
1805 list<Equation> eql = {};
1806 Boolean isThere = false;
1807 String str;
1808 DAE.ElementSource source;
1809
1810 // if we have no broken then we don't care!
1811 case {} then inEquations;
1812
1813 // if we have nothing toRemove then we don't care!
1814 case _
1815 algorithm
1816
2/2
✓ Branch 0 taken 856 times.
✓ Branch 1 taken 6 times.
862 for eq in inEquations loop
1817 eql := match eq
1818 case Equation.CONNECT(lhs = Expression.CREF(ty = _, cref = lhs),
1819 rhs = Expression.CREF(ty = _, cref = rhs), source = source)
1820 algorithm
1821 ✗ if not (isDeleted(lhs) or isDeleted(rhs)) then
1822 // check for equality
1823 isThere := false;
1824 ✗ for b in inBroken loop
1825 ✗ if ComponentRef.isEqual(b.lhs, lhs) and ComponentRef.isEqual(b.rhs, rhs) or
1826 ComponentRef.isEqual(b.rhs, lhs) and ComponentRef.isEqual(b.lhs, rhs)
1827 then
1828 isThere := true;
1829 break;
1830 end if;
1831 end for;
1832 end if;
1833 ✗ if not isThere then
1834 eql := eq :: eql;
1835 end if;
1836 then
1837 eql;
1838
1839 else eq :: eql;
1840 end match;
1841
1842 end for;
1843
1844 6 eql := listReverseInPlace(eql);
1845
1846
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if Flags.isSet(Flags.CGRAPH)
1847 then
1848 str := "";
1849 ✗ for b in inBroken loop
1850 ✗ str := str + "connect(" +
1851 ComponentRef.toString(b.lhs) + ", " +
1852 ComponentRef.toString(b.rhs) + ")\n";
1853 end for;
1854 ✗ print("- NFOCConnectionGraph.removeBrokenConnects:\n" + str + "\n");
1855 end if;
1856 then
1857 eql;
1858
1859 end match;
1860 end removeBrokenConnects;
1861
1862 function identifyConnectionsOperator
1863 input Absyn.Path functionName;
1864 output ConnectionsOperator call;
1865 algorithm
1866 call := match functionName
1867 local
1868 String name;
1869
1870 case Absyn.QUALIFIED(name = "Connections", path = Absyn.IDENT(name = name))
1871 then match name
1872 case "branch" then ConnectionsOperator.BRANCH;
1873 case "root" then ConnectionsOperator.ROOT;
1874 case "potentialRoot" then ConnectionsOperator.POTENTIAL_ROOT;
1875 case "isRoot" then ConnectionsOperator.IS_ROOT;
1876 case "rooted" then ConnectionsOperator.ROOTED;
1877 case "uniqueRoot" then ConnectionsOperator.UNIQUE_ROOT;
1878 case "uniqueRootIndices" then ConnectionsOperator.UNIQUE_ROOT_INDICES;
1879 else ConnectionsOperator.NOT_OPERATOR;
1880 end match;
1881
1882 case Absyn.IDENT(name = "rooted") then ConnectionsOperator.ROOTED;
1883 else ConnectionsOperator.NOT_OPERATOR;
1884 end match;
1885 end identifyConnectionsOperator;
1886
1887 function newCrefCrefTable
1888 output CrefCrefTable table;
1889 algorithm
1890 76 table := UnorderedMap.new<ComponentRef>(ComponentRef.hash, ComponentRef.isEqual);
1891 end newCrefCrefTable;
1892
1893 annotation(__OpenModelica_Interface="nf_frontend");
1894 end NFOCConnectionGraph;
1895