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 |