OMCompiler/Compiler/NFFrontEnd/NFArrayConnections.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 NFArrayConnections | ||
| 37 | import Connection = NFConnection; | ||
| 38 | import Connector = NFConnector; | ||
| 39 | import FlatModel = NFFlatModel; | ||
| 40 | import ComponentRef = NFComponentRef; | ||
| 41 | import Equation = NFEquation; | ||
| 42 | import Connections = NFConnections; | ||
| 43 | import Expression = NFExpression; | ||
| 44 | |||
| 45 | import SBSet; | ||
| 46 | import SBPWLinearMap; | ||
| 47 | |||
| 48 | protected | ||
| 49 | import SBGraph.IncidenceList; | ||
| 50 | import SBGraph.VertexDescriptor; | ||
| 51 | import Array; | ||
| 52 | import Call = NFCall; | ||
| 53 | import Ceval = NFCeval; | ||
| 54 | import Class = NFClass; | ||
| 55 | import NFClassTree.ClassTree; | ||
| 56 | import Component = NFComponent; | ||
| 57 | import DAE; | ||
| 58 | import Dimension = NFDimension; | ||
| 59 | import ElementSource; | ||
| 60 | import MetaModelica.Dangerous.*; | ||
| 61 | import NFInstNode.InstNode; | ||
| 62 | import NFInstNode; | ||
| 63 | import NFPrefixes.ConnectorType; | ||
| 64 | import NFPrefixes.Purity; | ||
| 65 | import NFPrefixes.Variability; | ||
| 66 | import NFBuiltin; | ||
| 67 | import Operator = NFOperator; | ||
| 68 | import Op = NFOperator.Op; | ||
| 69 | import SBFunctions; | ||
| 70 | import SBGraphUtil = NFSBGraphUtil; | ||
| 71 | import SimplifyExp = NFSimplifyExp; | ||
| 72 | import Subscript = NFSubscript; | ||
| 73 | import Type = NFType; | ||
| 74 | import Variable = NFVariable; | ||
| 75 | import UnorderedSet; | ||
| 76 | import UnorderedMap; | ||
| 77 | |||
| 78 | uniontype SetVertex | ||
| 79 | record SET_VERTEX | ||
| 80 | Connector name; | ||
| 81 | SBSet vs; | ||
| 82 | end SET_VERTEX; | ||
| 83 | |||
| 84 | function isEqual | ||
| 85 | input SetVertex v1; | ||
| 86 | input SetVertex v2; | ||
| 87 | output Boolean equal = Connector.isEqual(v1.name, v2.name); | ||
| 88 | end isEqual; | ||
| 89 | |||
| 90 | function toString | ||
| 91 | input SetVertex v; | ||
| 92 | output String str = Connector.toString(v.name) + "\n" + SBSet.toString(v.vs) + "\n"; | ||
| 93 | end toString; | ||
| 94 | end SetVertex; | ||
| 95 | |||
| 96 | uniontype SetEdge | ||
| 97 | record SET_EDGE | ||
| 98 | String name; | ||
| 99 | SBPWLinearMap es1; | ||
| 100 | SBPWLinearMap es2; | ||
| 101 | end SET_EDGE; | ||
| 102 | |||
| 103 | function isEqual | ||
| 104 | input SetEdge e1; | ||
| 105 | input SetEdge e2; | ||
| 106 | output Boolean equal = e1.name == e2.name; | ||
| 107 | end isEqual; | ||
| 108 | |||
| 109 | function toString | ||
| 110 | input SetEdge e; | ||
| 111 | output String str = e.name + "\n" + "SetVertex 1:\t" + SBPWLinearMap.toString(e.es1) + "\nSetVertex 2:\t" + SBPWLinearMap.toString(e.es2) + "\n"; | ||
| 112 | end toString; | ||
| 113 | end SetEdge; | ||
| 114 | |||
| 115 | public | ||
| 116 | type NameVertexTable = UnorderedMap<String, VertexDescriptor>; | ||
| 117 | type SBGraph = IncidenceList<SetVertex, SetEdge>; | ||
| 118 | type ConnVar = tuple<ComponentRef, Integer, String, Boolean> "variable, dimensions of its connector, path inside the connector, outside connector"; | ||
| 119 | type VertexList = list<VertexDescriptor>; | ||
| 120 | type VertexSets = UnorderedMap<SBAtomicSet, VertexDescriptor>; | ||
| 121 | |||
| 122 | function resolve | ||
| 123 | input output FlatModel flatModel; | ||
| 124 | protected | ||
| 125 | Integer max_dim = 1; | ||
| 126 | Vector<Integer> v_count, e_count; | ||
| 127 | list<Equation> conns; | ||
| 128 | list<list<Equation>> eqll = {}; | ||
| 129 | SBGraph graph; | ||
| 130 | SBPWLinearMap res; | ||
| 131 | NameVertexTable nmv_table; | ||
| 132 | array<list<VertexDescriptor>> comp_vertices; | ||
| 133 | array<list<Integer>> comp_edges; | ||
| 134 | array<list<ConnVar>> pot_vars, flow_vars; | ||
| 135 | array<InstNode> iterators; | ||
| 136 | list<Expression> iter_expl; | ||
| 137 | algorithm | ||
| 138 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 3 times.
|
54 | for var in flatModel.variables loop |
| 139 | 51 | max_dim := max(max_dim, Type.dimensionCount(var.ty)); | |
| 140 | end for; | ||
| 141 | |||
| 142 | 3 | v_count := Vector.newFill(max_dim, 1); | |
| 143 | 3 | e_count := Vector.newFill(max_dim, 1); | |
| 144 | |||
| 145 | 3 | (flatModel, conns) := collect(flatModel); | |
| 146 | |||
| 147 | 3 | graph := IncidenceList.new(SetVertex.isEqual, SetEdge.isEqual, SetVertex.toString, SetEdge.toString); | |
| 148 | 3 | nmv_table := UnorderedMap.new<VertexDescriptor>(stringHashDjb2, stringEq); | |
| 149 | 3 | createGraph(flatModel.variables, conns, graph, v_count, e_count, nmv_table); | |
| 150 | |||
| 151 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if Flags.isSet(Flags.DUMP_SET_BASED_GRAPHS) then |
| 152 | ✗ | print(IncidenceList.toString(graph)); | |
| 153 | end if; | ||
| 154 | |||
| 155 | 3 | iterators := arrayCreate(Vector.size(v_count), InstNode.EMPTY_NODE()); | |
| 156 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
7 | for i in 1:arrayLength(iterators) loop |
| 157 | 4 | iterators[i] := InstNode.newUniqueIterator(); | |
| 158 | end for; | ||
| 159 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 3 times.
|
14 | iter_expl := list(Expression.fromCref(ComponentRef.makeIterator(i, Type.INTEGER())) for i in iterators); |
| 160 | |||
| 161 | 3 | (pot_vars, flow_vars) := vertexVars(flatModel.variables, graph); | |
| 162 | 3 | (comp_vertices, comp_edges) := components(graph); | |
| 163 | |||
| 164 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
13 | for i in 1:arrayLength(comp_vertices) loop |
| 165 | 10 | res := componentMap(comp_vertices[i], comp_edges[i], graph); | |
| 166 | 10 | eqll := generateEquations(res, vertexSets(comp_vertices[i], graph), iterators, iter_expl, pot_vars, flow_vars) :: eqll; | |
| 167 | end for; | ||
| 168 | |||
| 169 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if Flags.isSet(Flags.DUMP_SET_BASED_GRAPHS) then |
| 170 | ✗ | print(IncidenceList.toString(graph)); | |
| 171 | end if; | ||
| 172 | |||
| 173 | 3 | flatModel.equations := listAppend(flatModel.equations, List.flatten(listReverseInPlace(eqll))); | |
| 174 | end resolve; | ||
| 175 | |||
| 176 | protected | ||
| 177 | function collect | ||
| 178 | input output FlatModel flatModel; | ||
| 179 | output list<Equation> conns = {}; | ||
| 180 | protected | ||
| 181 | list<Equation> eql = {}; | ||
| 182 | algorithm | ||
| 183 | 3 | (conns, eql) := List.splitOnTrue(flatModel.equations, isConnection); | |
| 184 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | flatModel.equations := eql; |
| 185 | end collect; | ||
| 186 | |||
| 187 | function isConnection | ||
| 188 | input Equation eq; | ||
| 189 | output Boolean isConn; | ||
| 190 | algorithm | ||
| 191 | isConn := match eq | ||
| 192 | local | ||
| 193 | Equation e; | ||
| 194 | |||
| 195 | case Equation.CONNECT() then true; | ||
| 196 | 14 | case Equation.FOR(body = e :: _) then isConnection(e); | |
| 197 | else false; | ||
| 198 | end match; | ||
| 199 | end isConnection; | ||
| 200 | |||
| 201 | function createGraph | ||
| 202 | input list<Variable> variables; | ||
| 203 | input list<Equation> equations; | ||
| 204 | input SBGraph graph; | ||
| 205 | input Vector<Integer> vCount; | ||
| 206 | input Vector<Integer> eCount; | ||
| 207 | input NameVertexTable nmvTable; | ||
| 208 | algorithm | ||
| 209 | 3 | addFlowsToGraph(variables, graph, vCount, nmvTable); | |
| 210 | 3 | addConnectionsToGraph(equations, graph, vCount, eCount, nmvTable); | |
| 211 | end createGraph; | ||
| 212 | |||
| 213 | function addFlowsToGraph | ||
| 214 | input list<Variable> variables; | ||
| 215 | input SBGraph graph; | ||
| 216 | input Vector<Integer> vCount; | ||
| 217 | input NameVertexTable nmvTable; | ||
| 218 | protected | ||
| 219 | Connector conn; | ||
| 220 | ComponentRef parent_cr; | ||
| 221 | algorithm | ||
| 222 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 3 times.
|
54 | for var in variables loop |
| 223 |
2/2✓ Branch 1 taken 21 times.
✓ Branch 2 taken 30 times.
|
51 | if Variable.isFlow(var) then |
| 224 | 21 | parent_cr := ComponentRef.rest(var.name); | |
| 225 | 21 | conn := Connector.fromFacedCref(parent_cr, ComponentRef.nodeType(parent_cr), NFConnector.Face.INSIDE, | |
| 226 | ElementSource.createElementSource(var.info)); | ||
| 227 | 21 | createVertex(conn, graph, vCount, nmvTable); | |
| 228 | end if; | ||
| 229 | end for; | ||
| 230 | end addFlowsToGraph; | ||
| 231 | |||
| 232 | function addConnectionsToGraph | ||
| 233 | input list<Equation> equations; | ||
| 234 | input SBGraph graph; | ||
| 235 | input Vector<Integer> vCount; | ||
| 236 | input Vector<Integer> eCount; | ||
| 237 | input NameVertexTable nmvTable; | ||
| 238 | protected | ||
| 239 | Expression range; | ||
| 240 | list<Equation> body; | ||
| 241 | algorithm | ||
| 242 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 9 times.
|
30 | for eq in equations loop |
| 243 | () := match eq | ||
| 244 | case Equation.CONNECT() | ||
| 245 | algorithm | ||
| 246 | 15 | createConnection(eq.lhs, eq.rhs, eq.source, graph, vCount, eCount, nmvTable); | |
| 247 | then | ||
| 248 | (); | ||
| 249 | |||
| 250 | case Equation.FOR(range = SOME(range)) | ||
| 251 | algorithm | ||
| 252 | 6 | range := Ceval.evalExp(range, Ceval.EvalTarget.new(Equation.info(eq), NFInstContext.ITERATION_RANGE)); | |
| 253 | |||
| 254 |
1/2✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | if not Type.isEmptyArray(Expression.typeOf(range)) then |
| 255 | 6 | body := Equation.replaceIteratorList(eq.body, eq.iterator, range); | |
| 256 | 6 | addConnectionsToGraph(body, graph, vCount, eCount, nmvTable); | |
| 257 | end if; | ||
| 258 | then | ||
| 259 | (); | ||
| 260 | |||
| 261 | else | ||
| 262 | algorithm | ||
| 263 | ✗ | Error.terminate(getInstanceName() + " got unknown equation " + | |
| 264 | Equation.toString(eq) + "\n", sourceInfo()); | ||
| 265 | ✗ | then | |
| 266 | fail(); | ||
| 267 | end match; | ||
| 268 | end for; | ||
| 269 | end addConnectionsToGraph; | ||
| 270 | |||
| 271 | function createConnection | ||
| 272 | input Expression lhs; | ||
| 273 | input Expression rhs; | ||
| 274 | input DAE.ElementSource source; | ||
| 275 | input SBGraph graph; | ||
| 276 | input Vector<Integer> vCount; | ||
| 277 | input Vector<Integer> eCount; | ||
| 278 | input NameVertexTable nmvTable; | ||
| 279 | protected | ||
| 280 | ComponentRef lhs_cr, rhs_cr; | ||
| 281 | list<Subscript> lhs_subs, rhs_subs; | ||
| 282 | SBMultiInterval mi1, mi2; | ||
| 283 | VertexDescriptor d1, d2; | ||
| 284 | Connector lhs_conn, rhs_conn; | ||
| 285 | algorithm | ||
| 286 | 15 | (lhs_cr, lhs_subs) := separate(Expression.toCref(lhs)); | |
| 287 | 15 | (rhs_cr, rhs_subs) := separate(Expression.toCref(rhs)); | |
| 288 | |||
| 289 | 15 | lhs_conn := Connector.fromCref(lhs_cr, ComponentRef.nodeType(lhs_cr), source); | |
| 290 | 15 | rhs_conn := Connector.fromCref(rhs_cr, ComponentRef.nodeType(rhs_cr), source); | |
| 291 | |||
| 292 | 15 | (mi1, d1) := getConnectIntervals(lhs_conn, lhs_subs, graph, vCount, nmvTable); | |
| 293 | 15 | (mi2, d2) := getConnectIntervals(rhs_conn, rhs_subs, graph, vCount, nmvTable); | |
| 294 | |||
| 295 | 15 | updateGraph(d1, d2, mi1, mi2, graph, eCount); | |
| 296 | end createConnection; | ||
| 297 | |||
| 298 | function separate | ||
| 299 | input output ComponentRef cref; | ||
| 300 | output list<Subscript> subs; | ||
| 301 | algorithm | ||
| 302 | 30 | cref := ComponentRef.fillSubscripts(cref); | |
| 303 | 30 | cref := ComponentRef.replaceWholeSubscripts(cref); | |
| 304 | 30 | subs := ComponentRef.subscriptsAllFlat(cref); | |
| 305 | 30 | cref := ComponentRef.stripSubscriptsAll(cref); | |
| 306 | end separate; | ||
| 307 | |||
| 308 | function getConnectIntervals | ||
| 309 | input Connector conn; | ||
| 310 | input list<Subscript> subs; | ||
| 311 | input SBGraph graph; | ||
| 312 | input Vector<Integer> vCount; | ||
| 313 | input NameVertexTable nmvTable; | ||
| 314 | output SBMultiInterval outMI; | ||
| 315 | output VertexDescriptor d; | ||
| 316 | algorithm | ||
| 317 | 30 | (outMI, d) := createVertex(conn, graph, vCount, nmvTable); | |
| 318 | 30 | outMI := SBGraphUtil.multiIntervalFromSubscripts(subs, vCount, outMI); | |
| 319 | end getConnectIntervals; | ||
| 320 | |||
| 321 | function createVertex | ||
| 322 | input Connector conn; | ||
| 323 | input SBGraph graph; | ||
| 324 | input Vector<Integer> vCount; | ||
| 325 | input NameVertexTable nmvTable; | ||
| 326 | output SBMultiInterval mi; | ||
| 327 | output VertexDescriptor d; | ||
| 328 | protected | ||
| 329 | Option<VertexDescriptor> od; | ||
| 330 | SetVertex v; | ||
| 331 | list<Dimension> dims; | ||
| 332 | SBSet s; | ||
| 333 | String name; | ||
| 334 | algorithm | ||
| 335 | 51 | name := Connector.toString(conn) + "$" + Connector.faceString(conn); | |
| 336 | 51 | od := UnorderedMap.get(name, nmvTable); | |
| 337 | |||
| 338 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 28 times.
|
51 | if isSome(od) then |
| 339 | 28 | SOME(d) := od; | |
| 340 | 28 | mi := vertexInterval(IncidenceList.getVertex(graph, d)); | |
| 341 | 28 | return; | |
| 342 | end if; | ||
| 343 | |||
| 344 | 23 | dims := crefDims(Connector.name(conn)); | |
| 345 | 23 | mi := SBGraphUtil.multiIntervalFromDimensions(dims, vCount); | |
| 346 | |||
| 347 | 23 | s := SBSet.newEmpty(); | |
| 348 | 23 | s := SBSet.addAtomicSet(SBAtomicSet.new(mi), s); | |
| 349 | |||
| 350 | 23 | v := SET_VERTEX(conn, s); | |
| 351 | 23 | d := IncidenceList.addVertex(graph, v); | |
| 352 | 23 | UnorderedMap.addUnique(name, d, nmvTable); | |
| 353 | end createVertex; | ||
| 354 | |||
| 355 | function vertexInterval | ||
| 356 | input SetVertex v; | ||
| 357 | output SBMultiInterval mi = SBAtomicSet.aset(UnorderedSet.first(SBSet.asets(v.vs))); | ||
| 358 | end vertexInterval; | ||
| 359 | |||
| 360 | function vertexSets | ||
| 361 | input list<VertexDescriptor> vertices; | ||
| 362 | input SBGraph graph; | ||
| 363 | output VertexSets sets = UnorderedMap.new<VertexDescriptor>(SBAtomicSet.hash, SBAtomicSet.isEqual); | ||
| 364 | protected | ||
| 365 | SetVertex v; | ||
| 366 | algorithm | ||
| 367 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 10 times.
|
33 | for d in vertices loop |
| 368 | 23 | v := IncidenceList.getVertex(graph, d); | |
| 369 | 23 | UnorderedMap.add(UnorderedSet.first(SBSet.asets(v.vs)), d, sets); | |
| 370 | end for; | ||
| 371 | end vertexSets; | ||
| 372 | |||
| 373 | function crefDims | ||
| 374 | input ComponentRef cr; | ||
| 375 | output list<Dimension> dims = {}; | ||
| 376 | protected | ||
| 377 | ComponentRef c = cr; | ||
| 378 | algorithm | ||
| 379 |
2/2✓ Branch 1 taken 220 times.
✓ Branch 2 taken 95 times.
|
315 | while not ComponentRef.isEmpty(c) loop |
| 380 | 220 | dims := listAppend(Type.arrayDims(ComponentRef.nodeType(c)), dims); | |
| 381 | 220 | c := ComponentRef.rest(c); | |
| 382 | end while; | ||
| 383 | end crefDims; | ||
| 384 | |||
| 385 | function updateGraph | ||
| 386 | input VertexDescriptor d1; | ||
| 387 | input VertexDescriptor d2; | ||
| 388 | input SBMultiInterval mi1; | ||
| 389 | input SBMultiInterval mi2; | ||
| 390 | input SBGraph graph; | ||
| 391 | input Vector<Integer> eCount; | ||
| 392 | protected | ||
| 393 | SBPWLinearMap pw1, pw2; | ||
| 394 | String name; | ||
| 395 | SetEdge se; | ||
| 396 | algorithm | ||
| 397 | 15 | (name, pw1, pw2) := SBGraphUtil.linearMapFromIntervals(d1, d2, mi1, mi2, eCount); | |
| 398 | 15 | se := SET_EDGE(name, pw1, pw2); | |
| 399 | 15 | IncidenceList.addEdge(graph, d1, d2, se); | |
| 400 | end updateGraph; | ||
| 401 | |||
| 402 | function components | ||
| 403 | "The vertices and edges of each connected component of the graph, ignoring | ||
| 404 | which elements an edge connects." | ||
| 405 | input SBGraph graph; | ||
| 406 | output array<list<VertexDescriptor>> vertices; | ||
| 407 | output array<list<Integer>> edges; | ||
| 408 | protected | ||
| 409 | Integer nv = IncidenceList.vertexCount(graph); | ||
| 410 | Integer ne = IncidenceList.edgeCount(graph); | ||
| 411 | Integer count = 0, r, c; | ||
| 412 | array<Integer> parent = listArray(List.intRange(nv)); | ||
| 413 | array<Integer> edge_vertex = arrayCreate(ne, 0); | ||
| 414 | array<Integer> comp = arrayCreate(nv, 0); | ||
| 415 | algorithm | ||
| 416 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
26 | for d in 1:nv loop |
| 417 |
2/2✓ Branch 1 taken 30 times.
✓ Branch 2 taken 23 times.
|
53 | for e in IncidenceList.getRow(graph, d) loop |
| 418 |
2/2✓ Branch 1 taken 15 times.
✓ Branch 2 taken 15 times.
|
30 | if edge_vertex[e] == 0 then |
| 419 | 15 | edge_vertex[e] := d; | |
| 420 | else | ||
| 421 | 15 | joinRoots(parent, d, edge_vertex[e]); | |
| 422 | end if; | ||
| 423 | end for; | ||
| 424 | end for; | ||
| 425 | |||
| 426 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
26 | for d in 1:nv loop |
| 427 | 23 | r := findRoot(parent, d); | |
| 428 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 13 times.
|
23 | if comp[r] == 0 then |
| 429 | 10 | count := count + 1; | |
| 430 | 10 | comp[r] := count; | |
| 431 | end if; | ||
| 432 | end for; | ||
| 433 | |||
| 434 | 3 | vertices := arrayCreate(count, {}); | |
| 435 | 3 | edges := arrayCreate(count, {}); | |
| 436 | |||
| 437 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
26 | for d in nv:-1:1 loop |
| 438 | 23 | c := comp[findRoot(parent, d)]; | |
| 439 | 46 | vertices[c] := d :: vertices[c]; | |
| 440 | end for; | ||
| 441 | |||
| 442 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
18 | for e in ne:-1:1 loop |
| 443 | 15 | c := comp[findRoot(parent, edge_vertex[e])]; | |
| 444 | 30 | edges[c] := e :: edges[c]; | |
| 445 | end for; | ||
| 446 | end components; | ||
| 447 | |||
| 448 | function findRoot | ||
| 449 | input array<Integer> parent; | ||
| 450 | input output Integer i; | ||
| 451 | protected | ||
| 452 | Integer p; | ||
| 453 | algorithm | ||
| 454 |
2/2✓ Branch 1 taken 33 times.
✓ Branch 2 taken 91 times.
|
124 | while parent[i] <> i loop |
| 455 | 33 | p := parent[parent[i]]; | |
| 456 | 33 | arrayUpdate(parent, i, p); | |
| 457 | i := p; | ||
| 458 | end while; | ||
| 459 | end findRoot; | ||
| 460 | |||
| 461 | function joinRoots | ||
| 462 | input array<Integer> parent; | ||
| 463 | input Integer i1; | ||
| 464 | input Integer i2; | ||
| 465 | protected | ||
| 466 | Integer r1 = findRoot(parent, i1), r2 = findRoot(parent, i2); | ||
| 467 | algorithm | ||
| 468 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if r1 < r2 then |
| 469 | ✗ | arrayUpdate(parent, r2, r1); | |
| 470 | elseif r2 < r1 then | ||
| 471 | 13 | arrayUpdate(parent, r1, r2); | |
| 472 | end if; | ||
| 473 | end joinRoots; | ||
| 474 | |||
| 475 | function componentMap | ||
| 476 | "Maps each element of the vertices of a component of the graph to the | ||
| 477 | smallest element it is connected to." | ||
| 478 | input list<VertexDescriptor> vertices; | ||
| 479 | input list<Integer> edges; | ||
| 480 | input SBGraph graph; | ||
| 481 | output SBPWLinearMap res; | ||
| 482 | protected | ||
| 483 | SBSet vss = SBSet.newEmpty(); | ||
| 484 | SetVertex v; | ||
| 485 | Boolean scalar = true; | ||
| 486 | SBPWLinearMap emap1, emap2; | ||
| 487 | array<Real> offset; | ||
| 488 | algorithm | ||
| 489 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 10 times.
|
33 | for d in vertices loop |
| 490 | 23 | v := IncidenceList.getVertex(graph, d); | |
| 491 | 23 | vss := SBSet.addAtomicSets(SBSet.asets(v.vs), vss); | |
| 492 |
5/6✓ Branch 0 taken 16 times.
✓ Branch 1 taken 7 times.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 6 times.
✓ Branch 9 taken 10 times.
|
23 | scalar := scalar and UnorderedSet.size(SBSet.asets(v.vs)) == 1 and |
| 493 | SBMultiInterval.size(vertexInterval(v)) == 1; | ||
| 494 | end for; | ||
| 495 | |||
| 496 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 9 times.
|
10 | if listEmpty(edges) then |
| 497 | 1 | res := SBPWLinearMap.newIdentity(vss); | |
| 498 | elseif scalar then | ||
| 499 | 3 | offset := Array.map(SBSet.minElem(vss), intReal); | |
| 500 | 3 | res := SBPWLinearMap.newScalar(vss, SBLinearMap.new(arrayCreate(arrayLength(offset), 0.0), offset)); | |
| 501 | else | ||
| 502 |
4/4✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 6 times.
|
18 | (emap1, emap2) := createMaps(list(IncidenceList.getEdge(graph, e) for e in edges)); |
| 503 | 6 | res := SBFunctions.connectedComponents(vss, emap1, emap2); | |
| 504 | end if; | ||
| 505 | end componentMap; | ||
| 506 | |||
| 507 | function createMaps | ||
| 508 | input list<SetEdge> edges; | ||
| 509 | output SBPWLinearMap emap1; | ||
| 510 | output SBPWLinearMap emap2; | ||
| 511 | protected | ||
| 512 | SetEdge e; | ||
| 513 | list<SetEdge> es; | ||
| 514 | algorithm | ||
| 515 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | e :: es := edges; |
| 516 | 6 | emap1 := e.es1; | |
| 517 | 6 | emap2 := e.es2; | |
| 518 | |||
| 519 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
|
12 | for e in es loop |
| 520 | 6 | emap1 := SBPWLinearMap.combine(e.es1, emap1); | |
| 521 | 6 | emap2 := SBPWLinearMap.combine(e.es2, emap2); | |
| 522 | end for; | ||
| 523 | end createMaps; | ||
| 524 | |||
| 525 | function vertexVars | ||
| 526 | "The potential and flow variables of the connector of each vertex, with the | ||
| 527 | number of dimensions of the connector and their path inside it." | ||
| 528 | input list<Variable> variables; | ||
| 529 | input SBGraph graph; | ||
| 530 | output array<list<ConnVar>> potVars; | ||
| 531 | output array<list<ConnVar>> flowVars; | ||
| 532 | protected | ||
| 533 | Integer nv = IncidenceList.vertexCount(graph); | ||
| 534 | UnorderedMap<ComponentRef, VertexList> names; | ||
| 535 | SetVertex v; | ||
| 536 | ComponentRef name; | ||
| 537 | list<VertexDescriptor> candidates; | ||
| 538 | Boolean is_pot; | ||
| 539 | ConnVar cv; | ||
| 540 | algorithm | ||
| 541 | 3 | potVars := arrayCreate(nv, {}); | |
| 542 | 3 | flowVars := arrayCreate(nv, {}); | |
| 543 | 3 | names := UnorderedMap.new<VertexList>(ComponentRef.hashStrip, ComponentRef.isEqualStrip); | |
| 544 | |||
| 545 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
26 | for d in 1:nv loop |
| 546 | 23 | v := IncidenceList.getVertex(graph, d); | |
| 547 | 23 | name := Connector.name(v.name); | |
| 548 | 46 | UnorderedMap.add(name, d :: UnorderedMap.getOrDefault(name, names, {}), names); | |
| 549 | end for; | ||
| 550 | |||
| 551 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 3 times.
|
54 | for var in variables loop |
| 552 | 51 | is_pot := Variable.isPotential(var); | |
| 553 | |||
| 554 |
4/4✓ Branch 0 taken 30 times.
✓ Branch 1 taken 21 times.
✓ Branch 3 taken 21 times.
✓ Branch 4 taken 9 times.
|
51 | if is_pot or Variable.isFlow(var) then |
| 555 | // ComponentRef.isPrefix only matches the variable itself or its parent. | ||
| 556 | 42 | candidates := UnorderedMap.getOrDefault(var.name, names, {}); | |
| 557 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
42 | if ComponentRef.isCref(var.name) then |
| 558 | 42 | candidates := listAppend(UnorderedMap.getOrDefault(ComponentRef.rest(var.name), names, {}), candidates); | |
| 559 | end if; | ||
| 560 | |||
| 561 |
2/2✓ Branch 0 taken 46 times.
✓ Branch 1 taken 42 times.
|
88 | for d in candidates loop |
| 562 | 46 | v := IncidenceList.getVertex(graph, d); | |
| 563 | 46 | name := Connector.name(v.name); | |
| 564 | |||
| 565 |
1/2✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
|
46 | if ComponentRef.isPrefix(name, var.name) then |
| 566 |
2/2✓ Branch 1 taken 72 times.
✓ Branch 2 taken 46 times.
|
118 | for field in recordFields(var.name) loop |
| 567 |
2/2✓ Branch 1 taken 64 times.
✓ Branch 2 taken 8 times.
|
136 | cv := (field, listLength(crefDims(name)), memberName(field, name), Connector.isOutside(v.name)); |
| 568 | |||
| 569 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 36 times.
|
72 | if is_pot then |
| 570 | 72 | potVars[d] := cv :: potVars[d]; | |
| 571 | else | ||
| 572 | 72 | flowVars[d] := cv :: flowVars[d]; | |
| 573 | end if; | ||
| 574 | end for; | ||
| 575 | end if; | ||
| 576 | end for; | ||
| 577 | end if; | ||
| 578 | end for; | ||
| 579 | |||
| 580 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
26 | for d in 1:nv loop |
| 581 | 23 | potVars[d] := listReverseInPlace(potVars[d]); | |
| 582 | 23 | flowVars[d] := listReverseInPlace(flowVars[d]); | |
| 583 | end for; | ||
| 584 | end vertexVars; | ||
| 585 | |||
| 586 | function recordFields | ||
| 587 | "The scalar fields of a record variable, or the variable itself if it is | ||
| 588 | not a record." | ||
| 589 | input ComponentRef cref; | ||
| 590 | input output list<ComponentRef> fields = {}; | ||
| 591 | protected | ||
| 592 | Type ty = Type.arrayElementType(ComponentRef.nodeType(cref)); | ||
| 593 | array<InstNode> comps; | ||
| 594 | Component c; | ||
| 595 | algorithm | ||
| 596 |
2/2✓ Branch 1 taken 26 times.
✓ Branch 2 taken 72 times.
|
98 | if Type.isRecord(ty) then |
| 597 | 26 | comps := ClassTree.getComponents(Class.classTree(InstNode.getClass(Type.complexNode(ty)))); | |
| 598 | |||
| 599 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
78 | for i in arrayLength(comps):-1:1 loop |
| 600 | 52 | c := InstNode.component(comps[i]); | |
| 601 | |||
| 602 |
1/2✓ Branch 2 taken 52 times.
✗ Branch 3 not taken.
|
52 | if not ConnectorType.isPotentiallyPresent(Component.connectorType(c)) then |
| 603 | 52 | fields := recordFields(ComponentRef.append(ComponentRef.fromNode(comps[i], Component.getType(c)), cref), fields); | |
| 604 | end if; | ||
| 605 | end for; | ||
| 606 | else | ||
| 607 | fields := cref :: fields; | ||
| 608 | end if; | ||
| 609 | end recordFields; | ||
| 610 | |||
| 611 | function generateEquations | ||
| 612 | input SBPWLinearMap pw; | ||
| 613 | input VertexSets vertexSets; | ||
| 614 | input array<InstNode> iterators; | ||
| 615 | input list<Expression> iterExps; | ||
| 616 | input array<list<ConnVar>> potVars; | ||
| 617 | input array<list<ConnVar>> flowVars; | ||
| 618 | output list<Equation> equations = {}; | ||
| 619 | protected | ||
| 620 | SBSet vc_im, aux_s, vc_domi, vc_domi_aux; | ||
| 621 | list<ConnVar> vars; | ||
| 622 | algorithm | ||
| 623 | 10 | vc_im := SBPWLinearMap.fullImage(pw); | |
| 624 | |||
| 625 |
2/2✓ Branch 3 taken 12 times.
✓ Branch 4 taken 10 times.
|
32 | for aset in UnorderedSet.toArray(SBSet.asets(vc_im)) loop |
| 626 | 12 | aux_s := SBSet.newEmpty(); | |
| 627 | 12 | aux_s := SBSet.addAtomicSet(aset, aux_s); | |
| 628 | 12 | vc_domi := SBPWLinearMap.preImage(pw, aux_s); | |
| 629 | 12 | vc_domi_aux := SBSet.complement(vc_domi, aux_s); | |
| 630 | 12 | vars := getVars(aset, vertexSets, potVars); | |
| 631 | |||
| 632 | 12 | equations := generatePotentialEquations(aset, vc_domi_aux, vars, iterators, | |
| 633 | iterExps, vertexSets, potVars, equations); | ||
| 634 | 12 | equations := generateFlowEquation(aset, vc_domi, iterators, vertexSets, flowVars, equations); | |
| 635 | end for; | ||
| 636 | |||
| 637 | 10 | equations := listReverseInPlace(equations); | |
| 638 | end generateEquations; | ||
| 639 | |||
| 640 | function intervalToRange | ||
| 641 | input SBInterval interval; | ||
| 642 | output Expression range; | ||
| 643 | protected | ||
| 644 | Integer lo = SBInterval.lowerBound(interval); | ||
| 645 | Integer hi = SBInterval.upperBound(interval); | ||
| 646 | algorithm | ||
| 647 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 9 times.
|
34 | if lo == hi then |
| 648 | 25 | range := Expression.INTEGER(lo); | |
| 649 | else | ||
| 650 | 9 | range := Expression.makeIntegerRange(lo, SBInterval.stepValue(interval), hi); | |
| 651 | end if; | ||
| 652 | end intervalToRange; | ||
| 653 | |||
| 654 | function generatePotentialEquations | ||
| 655 | input SBAtomicSet aset; | ||
| 656 | input SBSet dom; | ||
| 657 | input list<ConnVar> vars; | ||
| 658 | input array<InstNode> iterators; | ||
| 659 | input list<Expression> iterExps; | ||
| 660 | input VertexSets vertexSets; | ||
| 661 | input array<list<ConnVar>> potVars; | ||
| 662 | input output list<Equation> equations; | ||
| 663 | protected | ||
| 664 | SBMultiInterval mi, mi_range, aux_mi; | ||
| 665 | array<SBInterval> inters; | ||
| 666 | array<Expression> ranges; | ||
| 667 | list<ConnVar> vars1; | ||
| 668 | list<Equation> eql; | ||
| 669 | list<Expression> inds; | ||
| 670 | algorithm | ||
| 671 |
2/2✓ Branch 3 taken 18 times.
✓ Branch 4 taken 12 times.
|
42 | for auxi in UnorderedSet.toArray(SBSet.asets(dom)) loop |
| 672 | 18 | mi := SBAtomicSet.aset(auxi); | |
| 673 | 18 | mi_range := applyOffset(mi, getOffset(auxi, vertexSets)); | |
| 674 | 18 | inters := SBMultiInterval.intervals(mi_range); | |
| 675 | 18 | ranges := Array.map(inters, intervalToRange); | |
| 676 | |||
| 677 | 18 | vars1 := getVars(auxi, vertexSets, potVars); | |
| 678 | |||
| 679 | 18 | mi := SBAtomicSet.aset(aset); | |
| 680 | 18 | aux_mi := applyOffset(mi, getOffset(aset, vertexSets)); | |
| 681 | 18 | inds := transMulti(mi_range, aux_mi, iterators, false); | |
| 682 | |||
| 683 | 18 | eql := generatePotentialEquations2(vars1, vars, iterExps, inds); | |
| 684 | 18 | equations := generateForLoop(eql, iterators, ranges, equations); | |
| 685 | end for; | ||
| 686 | end generatePotentialEquations; | ||
| 687 | |||
| 688 | function generatePotentialEquations2 | ||
| 689 | input list<ConnVar> vars1; | ||
| 690 | input list<ConnVar> vars2; | ||
| 691 | input list<Expression> inds1; | ||
| 692 | input list<Expression> inds2; | ||
| 693 | output list<Equation> equations = {}; | ||
| 694 | protected | ||
| 695 | ComponentRef var1, var2; | ||
| 696 | Integer n1, n2; | ||
| 697 | String m1, m2; | ||
| 698 | Expression l, r; | ||
| 699 | Type ty; | ||
| 700 | Equation eq; | ||
| 701 | algorithm | ||
| 702 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 18 times.
|
43 | for v1 in vars1 loop |
| 703 | 25 | (var1, n1, m1, _) := v1; | |
| 704 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 25 times.
|
64 | for v2 in vars2 loop |
| 705 | 39 | (var2, n2, m2, _) := v2; | |
| 706 | // the same member of both connectors: a connector can have several | ||
| 707 | // potential variables of the same type (e.g. v and an angle theta) | ||
| 708 | // (the element types: the dimensions of a node can belong to the | ||
| 709 | // connector, e.g. u[3] for an array of input connectors) | ||
| 710 |
4/6✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 14 times.
✓ Branch 10 taken 25 times.
✗ Branch 11 not taken.
|
39 | if m1 == m2 and |
| 711 | Type.isEqual(Type.arrayElementType(ComponentRef.nodeType(var1)), Type.arrayElementType(ComponentRef.nodeType(var2))) then | ||
| 712 | 25 | l := generateConnector(var1, inds1, n1); | |
| 713 | 25 | r := generateConnector(var2, inds2, n2); | |
| 714 | 25 | ty := Expression.typeOf(l); | |
| 715 |
2/4✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25 times.
✗ Branch 5 not taken.
|
25 | if ComponentRef.variability(var1) > Variability.PARAMETER and ComponentRef.variability(var2) > Variability.PARAMETER then |
| 716 | 25 | eq := Equation.makeEquality(l, r, ty, scalarizeMode = NFEquation.ScalarizeMode.DONT_SCALARIZE); | |
| 717 | else | ||
| 718 | // connected constants and parameters are checked, like the classic connection handler does | ||
| 719 | ✗ | eq := makeEqualityAssert(l, r, ty); | |
| 720 | end if; | ||
| 721 | equations := eq :: equations; | ||
| 722 | end if; | ||
| 723 | end for; | ||
| 724 | end for; | ||
| 725 | |||
| 726 | 18 | equations := listReverseInPlace(equations); | |
| 727 | end generatePotentialEquations2; | ||
| 728 | |||
| 729 | function makeEqualityAssert | ||
| 730 | "assert(abs(l - r) <= 0) for Reals, assert(l == r) else; like | ||
| 731 | NFConnectEquations.makeEqualityAssert, for array connections" | ||
| 732 | input Expression l; | ||
| 733 | input Expression r; | ||
| 734 | input Type ty; | ||
| 735 | output Equation eq; | ||
| 736 | protected | ||
| 737 | Type elem_ty = Type.arrayElementType(ty); | ||
| 738 | Expression exp; | ||
| 739 | algorithm | ||
| 740 | ✗ | if Type.isArray(ty) then | |
| 741 | ✗ | Error.addInternalError(getInstanceName() + ": connected parameters of array type are not supported with array connections yet: " | |
| 742 | + Expression.toString(l) + ", " + Expression.toString(r), sourceInfo()); | ||
| 743 | ✗ | fail(); | |
| 744 | end if; | ||
| 745 | ✗ | if Type.isReal(elem_ty) then | |
| 746 | ✗ | exp := Expression.BINARY(l, Operator.makeSub(elem_ty), r); | |
| 747 | ✗ | exp := Expression.CALL(Call.makeTypedCall(NFBuiltinFuncs.ABS_REAL, {exp}, Expression.variability(exp), Purity.PURE)); | |
| 748 | ✗ | exp := Expression.RELATION(exp, Operator.makeLessEq(elem_ty), Expression.REAL(0.0), -1); | |
| 749 | else | ||
| 750 | ✗ | exp := Expression.RELATION(l, Operator.makeEqual(elem_ty), r, -1); | |
| 751 | end if; | ||
| 752 | ✗ | eq := Equation.ASSERT(exp, Expression.STRING("Connected constants/parameters must be equal"), | |
| 753 | NFBuiltin.ASSERTIONLEVEL_ERROR, NFInstNode.NO_SCOPE, DAE.emptyElementSource); | ||
| 754 | end makeEqualityAssert; | ||
| 755 | |||
| 756 | function generateFlowEquation | ||
| 757 | input SBAtomicSet aset; | ||
| 758 | input SBSet dom; | ||
| 759 | input array<InstNode> iterators; | ||
| 760 | input VertexSets vertexSets; | ||
| 761 | input array<list<ConnVar>> flowVars; | ||
| 762 | input output list<Equation> equations; | ||
| 763 | protected | ||
| 764 | SBMultiInterval mi, mi_range, mi_range2; | ||
| 765 | array<SBInterval> inters; | ||
| 766 | array<Expression> ranges; | ||
| 767 | list<Expression> expl, inds; | ||
| 768 | Boolean is_sum, outside; | ||
| 769 | list<ConnVar> vars; | ||
| 770 | ComponentRef var; | ||
| 771 | Integer n; | ||
| 772 | String m; | ||
| 773 | Expression e, sum_exp; | ||
| 774 | Type ty; | ||
| 775 | Equation eq; | ||
| 776 | UnorderedMap<String, ExpList> named_expl = UnorderedMap.new<ExpList>(stringHashDjb2, stringEq) "the terms of each sum, in reverse order"; | ||
| 777 | UnorderedSet<String> elementwise = UnorderedSet.new(stringHashDjb2, stringEq); | ||
| 778 | list<tuple<ComponentRef, Integer, String, Boolean, list<Expression>, Boolean>> terms = {}; | ||
| 779 | Integer sz; | ||
| 780 | algorithm | ||
| 781 | 12 | mi := SBAtomicSet.aset(aset); | |
| 782 | 12 | mi_range := applyOffset(mi, getOffset(aset, vertexSets)); | |
| 783 | 12 | inters := SBMultiInterval.intervals(mi_range); | |
| 784 | 12 | ranges := Array.map(inters, intervalToRange); | |
| 785 | expl := {}; | ||
| 786 | |||
| 787 | // collect the flow variables of all connectors of the set with their indices | ||
| 788 |
2/2✓ Branch 3 taken 30 times.
✓ Branch 4 taken 12 times.
|
54 | for auxi in UnorderedSet.toArray(SBSet.asets(dom)) loop |
| 789 | 30 | mi := SBAtomicSet.aset(auxi); | |
| 790 | 30 | mi_range2 := applyOffset(mi, getOffset(auxi, vertexSets)); | |
| 791 | 30 | (inds, is_sum) := transMulti(mi_range, mi_range2, iterators, true); | |
| 792 | 30 | vars := getVars(auxi, vertexSets, flowVars); | |
| 793 | |||
| 794 |
2/2✓ Branch 0 taken 43 times.
✓ Branch 1 taken 30 times.
|
73 | for v in vars loop |
| 795 | 43 | (var, n, m, outside) := v; | |
| 796 |
4/4✓ Branch 0 taken 40 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 4 times.
|
122 | terms := (var, n, m, outside, inds, is_sum) :: terms; |
| 797 | // a flow variable that is an array in its connector (e.g. i[3]) summed | ||
| 798 | // over a range of connectors has to be summed element by element | ||
| 799 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 40 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
|
43 | if is_sum and Type.isArray(ComponentRef.nodeType(var)) then |
| 800 | ✗ | UnorderedSet.add(m, elementwise); | |
| 801 | end if; | ||
| 802 | end for; | ||
| 803 | end for; | ||
| 804 | |||
| 805 |
2/2✓ Branch 1 taken 43 times.
✓ Branch 2 taken 12 times.
|
55 | for t in listReverse(terms) loop |
| 806 | 43 | (var, n, m, outside, inds, is_sum) := t; | |
| 807 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 43 times.
|
43 | if UnorderedSet.contains(m, elementwise) then |
| 808 | ✗ | sz := memberSize(var); | |
| 809 | ✗ | for k in 1:sz loop | |
| 810 | ✗ | e := flowTerm(ComponentRef.setSubscripts({Subscript.INDEX(Expression.INTEGER(k))}, var), inds, n, is_sum, outside); | |
| 811 | ✗ | addNamed(m + "[" + intString(k) + "]", e, named_expl); | |
| 812 | end for; | ||
| 813 | else | ||
| 814 | 43 | e := flowTerm(var, inds, n, is_sum, outside); | |
| 815 | 43 | addNamed(m, e, named_expl); | |
| 816 | end if; | ||
| 817 | end for; | ||
| 818 | |||
| 819 | // one sum for each flow variable of the connectors (a connector can have several), | ||
| 820 | // in the order of their first terms | ||
| 821 |
2/2✓ Branch 1 taken 18 times.
✓ Branch 2 taken 12 times.
|
30 | for name in UnorderedMap.keyList(named_expl) loop |
| 822 | 18 | expl := UnorderedMap.getOrFail(name, named_expl); | |
| 823 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
18 | sum_exp :: expl := expl; |
| 824 | |||
| 825 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 18 times.
|
43 | while not listEmpty(expl) loop |
| 826 | 25 | e :: expl := expl; | |
| 827 | 25 | sum_exp := Expression.BINARY(e, Operator.makeAdd(Expression.typeOf(e)), sum_exp); | |
| 828 | end while; | ||
| 829 | |||
| 830 | 18 | ty := Expression.typeOf(sum_exp); | |
| 831 | 18 | eq := Equation.makeEquality(sum_exp, Expression.makeZero(ty), ty); | |
| 832 | 18 | equations := generateForLoop({eq}, iterators, ranges, equations); | |
| 833 | end for; | ||
| 834 | end generateFlowEquation; | ||
| 835 | |||
| 836 | function flowTerm | ||
| 837 | "A flow variable of a connector in the flow equation of its set, summed if a | ||
| 838 | range of connectors is connected to one." | ||
| 839 | input ComponentRef var; | ||
| 840 | input list<Expression> inds; | ||
| 841 | input Integer connectorDims; | ||
| 842 | input Boolean isSum; | ||
| 843 | input Boolean outside; | ||
| 844 | output Expression e; | ||
| 845 | algorithm | ||
| 846 | 43 | e := generateConnector(var, inds, connectorDims); | |
| 847 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 40 times.
|
43 | if isSum then |
| 848 |
2/4✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
|
3 | if Type.isArray(Expression.typeOf(e)) and Type.dimensionCount(Expression.typeOf(e)) > 1 then |
| 849 | ✗ | Error.addInternalError(getInstanceName() + ": the flow variable " + ComponentRef.toString(var) + | |
| 850 | " has several dimensions inside its connector, connecting a range of such connectors to one is not supported yet.", sourceInfo()); | ||
| 851 | ✗ | fail(); | |
| 852 | end if; | ||
| 853 | 6 | e := Expression.CALL(Call.makeTypedCall(NFBuiltinFuncs.SUM, | |
| 854 | {e}, Expression.variability(e), Purity.PURE, Type.arrayElementType(Expression.typeOf(e)))); | ||
| 855 | end if; | ||
| 856 | |||
| 857 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 4 times.
|
43 | if outside then |
| 858 | 4 | e := Expression.negate(e); | |
| 859 | end if; | ||
| 860 | end flowTerm; | ||
| 861 | |||
| 862 | function memberSize | ||
| 863 | "The size of a flow variable that is a vector in its connector." | ||
| 864 | input ComponentRef var; | ||
| 865 | output Integer sz; | ||
| 866 | protected | ||
| 867 | list<Dimension> dims = Type.arrayDims(ComponentRef.nodeType(var)); | ||
| 868 | algorithm | ||
| 869 | ✗ | if listLength(dims) <> 1 or not Dimension.isKnown(listHead(dims)) then | |
| 870 | ✗ | Error.addInternalError(getInstanceName() + ": the flow variable " + ComponentRef.toString(var) + | |
| 871 | " has to be a vector of known size inside its connector to connect a range of connectors to one.", sourceInfo()); | ||
| 872 | ✗ | fail(); | |
| 873 | end if; | ||
| 874 | ✗ | sz := Dimension.size(listHead(dims)); | |
| 875 | end memberSize; | ||
| 876 | |||
| 877 | type ExpList = list<Expression>; | ||
| 878 | |||
| 879 | function addNamed | ||
| 880 | "adds a term to the sum of the flow variable name" | ||
| 881 | input String name; | ||
| 882 | input Expression e; | ||
| 883 | input UnorderedMap<String, ExpList> namedExpl; | ||
| 884 | algorithm | ||
| 885 | 43 | UnorderedMap.addUpdate(name, function prependTerm(e = e), namedExpl); | |
| 886 | end addNamed; | ||
| 887 | |||
| 888 | function prependTerm | ||
| 889 | input Option<list<Expression>> old; | ||
| 890 | input Expression e; | ||
| 891 | output list<Expression> res = e :: Util.getOptionOrDefault(old, {}); | ||
| 892 | end prependTerm; | ||
| 893 | |||
| 894 | function generateConnector | ||
| 895 | "The variable of a connector, subscripted with the indices of the connector. | ||
| 896 | Dimensions of the variable inside the connector (e.g. v[3] in the connector) | ||
| 897 | stay whole." | ||
| 898 | input ComponentRef cr; | ||
| 899 | input list<Expression> indices; | ||
| 900 | input Integer connectorDims "the number of dimensions of the connector"; | ||
| 901 | output Expression outExp; | ||
| 902 | protected | ||
| 903 | list<Subscript> subs; | ||
| 904 | algorithm | ||
| 905 | 93 | outExp := Expression.fromCref(cr); | |
| 906 | |||
| 907 |
3/4✓ Branch 2 taken 46 times.
✓ Branch 3 taken 47 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 47 times.
|
93 | if Type.isArray(Expression.typeOf(outExp)) and connectorDims > 0 then |
| 908 |
4/4✓ Branch 0 taken 55 times.
✓ Branch 1 taken 47 times.
✓ Branch 2 taken 55 times.
✓ Branch 3 taken 47 times.
|
102 | subs := list(Subscript.fromTypedExp(i) for i in indices); |
| 909 | 47 | subs := List.firstN(subs, intMin(connectorDims, Type.dimensionCount(Expression.typeOf(outExp)))); | |
| 910 | 47 | outExp := Expression.applySubscripts(subs, outExp); | |
| 911 | end if; | ||
| 912 | end generateConnector; | ||
| 913 | |||
| 914 | function generateForLoop | ||
| 915 | input list<Equation> connects; | ||
| 916 | input array<InstNode> iterators; | ||
| 917 | input array<Expression> ranges; | ||
| 918 | input output list<Equation> equations; | ||
| 919 | protected | ||
| 920 | list<Equation> body = connects; | ||
| 921 | algorithm | ||
| 922 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
76 | for i in arrayLength(iterators):-1:1 loop |
| 923 |
2/2✓ Branch 2 taken 29 times.
✓ Branch 3 taken 11 times.
|
40 | if Expression.isInteger(ranges[i]) then |
| 924 | // Scalar range means the interval had the same lower and upper bound, | ||
| 925 | // in which case the iterator can be replaced with the scalar expression | ||
| 926 | // instead of creating an unnecessary for loop here. | ||
| 927 | 29 | body := Equation.replaceIteratorList(body, iterators[i], ranges[i]); | |
| 928 | else | ||
| 929 | 22 | body := {Equation.FOR(iterators[i], SOME(ranges[i]), body, NFInstNode.NO_SCOPE, DAE.emptyElementSource)}; | |
| 930 | end if; | ||
| 931 | end for; | ||
| 932 | |||
| 933 | 36 | equations := List.append_reverse(body, equations); | |
| 934 | end generateForLoop; | ||
| 935 | |||
| 936 | function getOffset | ||
| 937 | "The smallest element of the vertex containing aset." | ||
| 938 | input SBAtomicSet aset; | ||
| 939 | input VertexSets vertexSets; | ||
| 940 | output array<Integer> res = listArray({}); | ||
| 941 | protected | ||
| 942 | SBAtomicSet vset; | ||
| 943 | algorithm | ||
| 944 |
2/2✓ Branch 1 taken 48 times.
✓ Branch 2 taken 30 times.
|
78 | if UnorderedMap.contains(aset, vertexSets) then |
| 945 | 48 | res := SBAtomicSet.minElem(aset); | |
| 946 | 48 | return; | |
| 947 | end if; | ||
| 948 | |||
| 949 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
|
64 | for i in 1:UnorderedMap.size(vertexSets) loop |
| 950 | 64 | vset := UnorderedMap.keyAt(vertexSets, i); | |
| 951 | |||
| 952 |
2/2✓ Branch 2 taken 30 times.
✓ Branch 3 taken 34 times.
|
64 | if not SBAtomicSet.isEmpty(SBAtomicSet.intersection(aset, vset)) then |
| 953 | 30 | res := SBAtomicSet.minElem(vset); | |
| 954 | 30 | return; | |
| 955 | end if; | ||
| 956 | end for; | ||
| 957 | end getOffset; | ||
| 958 | |||
| 959 | function applyOffset | ||
| 960 | input SBMultiInterval mi; | ||
| 961 | input array<Integer> off; | ||
| 962 | output SBMultiInterval outMI; | ||
| 963 | protected | ||
| 964 | array<SBInterval> ints, res; | ||
| 965 | SBInterval i; | ||
| 966 | Integer o; | ||
| 967 | algorithm | ||
| 968 |
2/4✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 78 times.
|
156 | if SBMultiInterval.ndim(mi) <> arrayLength(off) or arrayEmpty(off) then |
| 969 | ✗ | outMI := SBMultiInterval.newEmpty(); | |
| 970 | else | ||
| 971 | 78 | ints := SBMultiInterval.intervals(mi); | |
| 972 | 78 | res := arrayCreateNoInit(arrayLength(ints), ints[1]); | |
| 973 | |||
| 974 |
1/2✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
|
167 | for j in 1:arrayLength(ints) loop |
| 975 | 89 | i := ints[j]; | |
| 976 | 89 | o := off[j]; | |
| 977 | 89 | res[j] := SBInterval.new(SBInterval.lowerBound(i) - o + 1, | |
| 978 | SBInterval.stepValue(i), | ||
| 979 | SBInterval.upperBound(i) - o + 1); | ||
| 980 | end for; | ||
| 981 | |||
| 982 | 78 | outMI := SBMultiInterval.fromArray(res); | |
| 983 | end if; | ||
| 984 | end applyOffset; | ||
| 985 | |||
| 986 | function memberName | ||
| 987 | "The path of a connector variable inside its connector, e.g. v for line.a.v | ||
| 988 | of the connector line.a, empty if the connector is the variable itself | ||
| 989 | (connector RealInput = input Real)." | ||
| 990 | input ComponentRef var; | ||
| 991 | input ComponentRef conn; | ||
| 992 | output String name; | ||
| 993 | protected | ||
| 994 | list<String> var_names = crefNames(var); | ||
| 995 | algorithm | ||
| 996 |
1/2✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
|
240 | for i in 1:listLength(crefNames(conn)) loop |
| 997 | 168 | var_names := List.restOrEmpty(var_names); | |
| 998 | end for; | ||
| 999 | 72 | name := stringDelimitList(var_names, "."); | |
| 1000 | end memberName; | ||
| 1001 | |||
| 1002 | function crefNames | ||
| 1003 | "The identifiers of a cref from the outermost one, without subscripts." | ||
| 1004 | input ComponentRef cref; | ||
| 1005 | output list<String> names = {}; | ||
| 1006 | protected | ||
| 1007 | ComponentRef c = cref; | ||
| 1008 | algorithm | ||
| 1009 |
2/2✓ Branch 1 taken 460 times.
✓ Branch 2 taken 144 times.
|
604 | while not ComponentRef.isEmpty(c) loop |
| 1010 | 460 | names := ComponentRef.firstName(c) :: names; | |
| 1011 | 460 | c := ComponentRef.rest(c); | |
| 1012 | end while; | ||
| 1013 | end crefNames; | ||
| 1014 | |||
| 1015 | function getVars | ||
| 1016 | "The variables of the connectors of the vertices that intersect aset, with | ||
| 1017 | the number of dimensions of their connector and their path inside it." | ||
| 1018 | input SBAtomicSet aset; | ||
| 1019 | input VertexSets vertexSets; | ||
| 1020 | input array<list<ConnVar>> vertexVars; | ||
| 1021 | output list<ConnVar> res; | ||
| 1022 | protected | ||
| 1023 | Option<VertexDescriptor> od; | ||
| 1024 | VertexDescriptor d; | ||
| 1025 | list<list<ConnVar>> varsl = {}; | ||
| 1026 | algorithm | ||
| 1027 | 60 | od := UnorderedMap.get(aset, vertexSets); | |
| 1028 | |||
| 1029 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 36 times.
|
60 | if isSome(od) then |
| 1030 | 36 | SOME(d) := od; | |
| 1031 | 36 | res := vertexVars[d]; | |
| 1032 | 36 | return; | |
| 1033 | end if; | ||
| 1034 | |||
| 1035 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
102 | for i in 1:UnorderedMap.size(vertexSets) loop |
| 1036 |
2/2✓ Branch 3 taken 24 times.
✓ Branch 4 taken 54 times.
|
78 | if not SBAtomicSet.isEmpty(SBAtomicSet.intersection(aset, UnorderedMap.keyAt(vertexSets, i))) then |
| 1037 | 24 | varsl := vertexVars[UnorderedMap.valueAt(vertexSets, i)] :: varsl; | |
| 1038 | end if; | ||
| 1039 | end for; | ||
| 1040 | |||
| 1041 | 24 | res := List.flatten(listReverseInPlace(varsl)); | |
| 1042 | end getVars; | ||
| 1043 | |||
| 1044 | function transMulti | ||
| 1045 | input SBMultiInterval mi1; | ||
| 1046 | input SBMultiInterval mi2; | ||
| 1047 | input array<InstNode> iterators; | ||
| 1048 | input Boolean forFlow; | ||
| 1049 | output list<Expression> outExpl = {}; | ||
| 1050 | output Boolean flowRange = false; | ||
| 1051 | protected | ||
| 1052 | array<SBInterval> ints1, ints2; | ||
| 1053 | SBInterval i1, i2; | ||
| 1054 | Integer i1_sz, i2_sz, m_int; | ||
| 1055 | Expression x, m, h, e; | ||
| 1056 | algorithm | ||
| 1057 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
|
48 | if SBMultiInterval.ndim(mi1) <> SBMultiInterval.ndim(mi2) then |
| 1058 | ✗ | return; | |
| 1059 | end if; | ||
| 1060 | |||
| 1061 | 48 | ints1 := SBMultiInterval.intervals(mi1); | |
| 1062 | 48 | ints2 := SBMultiInterval.intervals(mi2); | |
| 1063 | |||
| 1064 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
103 | for i in 1:arrayLength(ints1) loop |
| 1065 | 55 | i1 := ints1[i]; | |
| 1066 | 55 | i2 := ints2[i]; | |
| 1067 | 55 | i1_sz := SBInterval.size(i1); | |
| 1068 | 55 | i2_sz := SBInterval.size(i2); | |
| 1069 | |||
| 1070 | 55 | x := Expression.fromCref(ComponentRef.makeIterator(iterators[i], Type.INTEGER())); | |
| 1071 | |||
| 1072 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 4 times.
|
55 | if i1_sz == i2_sz then |
| 1073 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 51 times.
|
51 | m_int := intDiv(SBInterval.stepValue(i2), SBInterval.stepValue(i1)); |
| 1074 | 51 | m := Expression.INTEGER(m_int); | |
| 1075 | 51 | h := Expression.INTEGER(-m_int * SBInterval.lowerBound(i1) + SBInterval.lowerBound(i2)); | |
| 1076 | |||
| 1077 | // m * x + h | ||
| 1078 | 51 | e := Expression.BINARY( | |
| 1079 | Expression.BINARY(m, Operator.makeMul(Type.INTEGER()), x), | ||
| 1080 | Operator.makeAdd(Type.INTEGER()), | ||
| 1081 | h | ||
| 1082 | ); | ||
| 1083 | |||
| 1084 | outExpl := e :: outExpl; | ||
| 1085 | elseif i2_sz == 1 and not forFlow then | ||
| 1086 | 2 | outExpl := Expression.INTEGER(SBInterval.lowerBound(i2)) :: outExpl; | |
| 1087 | elseif i1_sz == 1 and forFlow then | ||
| 1088 | 2 | e := Expression.makeIntegerRange( | |
| 1089 | SBInterval.lowerBound(i2), SBInterval.stepValue(i2), SBInterval.upperBound(i2)); | ||
| 1090 | outExpl := e :: outExpl; | ||
| 1091 | flowRange := true; | ||
| 1092 | else | ||
| 1093 | ✗ | Error.terminate(getInstanceName() + " got invalid intervals.", sourceInfo()); | |
| 1094 | end if; | ||
| 1095 | end for; | ||
| 1096 | |||
| 1097 | 48 | outExpl := listReverseInPlace(outExpl); | |
| 1098 | end transMulti; | ||
| 1099 | |||
| 1100 | annotation(__OpenModelica_Interface="nf_frontend"); | ||
| 1101 | end NFArrayConnections; | ||
| 1102 |