Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 62.6% 1059 / 0 / 1693
Functions: -% 0 / 1 / 1
Branches: 54.3% 684 / 0 / 1260

OMCompiler/Compiler/BackEnd/Tearing.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 Tearing
37 " file: Tearing.mo
38 package: Tearing
39 description: Tearing contains functions used for tear strong connected components.
40 Implemented Methods are:
41 - omc tearing developed by TU Dresden: Frenkel,Schubert
42 - Cellier Tearing"
43
44
45 import BackendDAE;
46 import DAE;
47
48 protected
49 import AdjacencyMatrix;
50 import Array;
51 import BackendDAEEXT;
52 import BackendDAEOptimize;
53 import BackendDAEUtil;
54 import BackendDump;
55 import BackendEquation;
56 import BackendVariable;
57 import ComponentReference;
58 import ComponentReferenceBasics;
59 import Config;
60 import DoubleEnded;
61 import DumpGraphML;
62 import Error;
63 import ExecStat.execStat;
64 import Expression;
65 import ExpressionBasics;
66 import ExpressionSimplify;
67 import ExpressionSolve;
68 import Flags;
69 import GCExt;
70 import Global;
71 import List;
72 import Matching;
73 import MetaModelica.Dangerous;
74 import Mutable;
75 import UnorderedSet;
76 import Util;
77 import Sorting;
78 import System;
79 import ElementSource;
80 import SimCodeCodegenUtil;
81
82 // =============================================================================
83 // section for type definitions
84 //
85 //
86 // =============================================================================
87
88 protected constant String BORDER = "****************************************";
89 protected constant String UNDERLINE = "========================================";
90 protected constant list<String> withLSS = {"C", "wasm-jit", "wasm"} "targets that provide a linear sparse solver";
91 protected constant list<String> withNSS = {"C", "wasm-jit", "wasm"} "targets that provide a nonlinear sparse solver";
92
93 uniontype TearingMethod
94 record MINIMAL_TEARING "Only tear discrete variables from loops"
95 end MINIMAL_TEARING;
96 record OMC_TEARING end OMC_TEARING;
97 record CELLIER_TEARING end CELLIER_TEARING;
98 record TOTAL_TEARING end TOTAL_TEARING;
99 record USER_DEFINED_TEARING end USER_DEFINED_TEARING;
100 end TearingMethod;
101
102 // =============================================================================
103 // section for all public functions
104 //
105 // main function to divide to the selected tearing method
106 // =============================================================================
107
108 public function tearingSystem "author: Frenkel TUD 2012-05"
109 input BackendDAE.BackendDAE inDAE;
110 output BackendDAE.BackendDAE outDAE;
111 protected
112 String methodString = Config.getTearingMethod();
113 TearingMethod method;
114 BackendDAE.BackendDAEType DAEtype;
115 Integer strongComponentIndex = System.tmpTickIndex(Global.strongComponent_index);
116 algorithm
117 // Check if maxSizeLinearTearing maxSizeNonlinearTearing flag is illegal
118
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3938 times.
3938 if (Flags.getConfigInt(Flags.MAX_SIZE_LINEAR_TEARING) < 0) then
119 ✗ Error.addMessage(Error.INVALID_FLAG_TYPE, {"maxSizeLinearTearing", "non-negative integer", intString(Flags.getConfigInt(Flags.MAX_SIZE_LINEAR_TEARING))});
120 ✗ fail();
121 elseif (Flags.getConfigInt(Flags.MAX_SIZE_NONLINEAR_TEARING) < 0) then
122 ✗ Error.addMessage(Error.INVALID_FLAG_TYPE, {"maxSizeNonlinearTearing", "non-negative integer", intString(Flags.getConfigInt(Flags.MAX_SIZE_NONLINEAR_TEARING))});
123 ✗ fail();
124 end if;
125
126 // get method function and traverse systems
127 try
128 3938 method := getTearingMethod(methodString);
129
3/4
✓ Branch 1 taken 3936 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3936 times.
3938 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
130 2 BackendDAE.SHARED(backendDAEType=DAEtype) := inDAE.shared;
131 2 print("\n\n\n\n" + UNDERLINE + UNDERLINE + "\nCalling Tearing for " +
132 BackendDump.printBackendDAEType2String(DAEtype) +
133 "!\n" + UNDERLINE + UNDERLINE + "\n");
134 end if;
135 3938 (outDAE, strongComponentIndex) := BackendDAEUtil.mapEqSystemAndFold(inDAE, function tearingSystemWork(tearingMethod = method), strongComponentIndex);
136 3938 System.tmpTickSetIndex(strongComponentIndex, Global.strongComponent_index);
137 else
138 ✗ Error.addInternalError(getInstanceName() + " failed", sourceInfo());
139 ✗ fail();
140 end try;
141 end tearingSystem;
142
143 // =============================================================================
144 // protected
145 //
146 //
147 // =============================================================================
148
149 protected function getTearingMethod
150 input String inTearingMethod;
151 output TearingMethod outTearingMethod;
152 algorithm
153 outTearingMethod := match inTearingMethod
154 case "minimalTearing" then MINIMAL_TEARING();
155 case "omcTearing" then OMC_TEARING();
156 case "cellier" then CELLIER_TEARING();
157
158 else algorithm
159 ✗ Error.addInternalError(getInstanceName() + " got invalid name \"" + inTearingMethod + "\".", sourceInfo());
160 ✗ then fail();
161 end match;
162 end getTearingMethod;
163
164 protected function callTearingMethod
165 input TearingMethod inTearingMethod;
166 input BackendDAE.EqSystem isyst;
167 input BackendDAE.Shared ishared;
168 input list<Integer> eindex;
169 input list<Integer> vindx;
170 input BackendDAE.FullJacobian ojac;
171 input BackendDAE.JacobianType jacType;
172 input Boolean mixedSystem;
173 input Integer strongComponentIndex;
174 output BackendDAE.StrongComponent ocomp;
175 output Boolean outRunMatching;
176 protected
177 constant Boolean debug = false;
178 list<Integer> userTVars = {}, userResiduals = {};
179 TearingMethod tearingMethod = inTearingMethod;
180 algorithm
181
182 // Check for total tearing for this component
183
2/2
✓ Branch 2 taken 2025 times.
✓ Branch 3 taken 1 time.
2026 if listMember(strongComponentIndex, Flags.getConfigIntList(Flags.TOTAL_TEARING)) then
184 tearingMethod := TOTAL_TEARING();
185 else
186 // Get users tearing sets if existing
187 2025 userTVars := Flags.getConfigIntList(Flags.SET_TEARING_VARS);
188 2025 userResiduals := Flags.getConfigIntList(Flags.SET_RESIDUAL_EQNS);
189 2025 (userTVars, userResiduals) := getUserTearingSet(userTVars, userResiduals, strongComponentIndex);
190
191 // Check for user defined tearing for this component
192
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2024 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
2025 if not listEmpty(userTVars) and not listEmpty(userResiduals) then
193 tearingMethod := USER_DEFINED_TEARING();
194 end if;
195 end if;
196
197 // Call the appropriate tearing method
198 (ocomp, outRunMatching) := match tearingMethod
199 case OMC_TEARING()
200 algorithm
201
2/4
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35 times.
35 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
202 ✗ print("\nTearing type: heuristic\n");
203 ✗ print("Tearing strictness: " + Flags.getConfigString(Flags.TEARING_STRICTNESS) + "\n");
204 end if;
205 35 (ocomp,outRunMatching) := omcTearing(isyst, ishared, eindex, vindx, ojac, jacType, mixedSystem);
206 if debug then execStat("Tearing.omcTearing"); end if;
207 then (ocomp,outRunMatching);
208
209 case CELLIER_TEARING()
210 algorithm
211
3/4
✓ Branch 1 taken 1954 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1954 times.
1956 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
212 2 print("\nTearing type: heuristic\n");
213 2 print("Tearing strictness: " + Flags.getConfigString(Flags.TEARING_STRICTNESS) + "\n");
214 end if;
215 1956 (ocomp,outRunMatching) := CellierTearing(isyst, ishared, eindex, vindx, userTVars, ojac, jacType, mixedSystem, strongComponentIndex);
216 if debug then execStat("Tearing.CellierTearing"); end if;
217 then (ocomp,outRunMatching);
218
219 case TOTAL_TEARING()
220 algorithm
221
2/4
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
1 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
222 ✗ print("\nTearing type: total\n");
223 ✗ print("Tearing strictness: " + Flags.getConfigString(Flags.TEARING_STRICTNESS) + "\n");
224 end if;
225 1 (ocomp,outRunMatching) := totalTearing(isyst, ishared, eindex, vindx, ojac, jacType, mixedSystem);
226 if debug then execStat("Tearing.totalTearing"); end if;
227 then (ocomp,outRunMatching);
228
229 case MINIMAL_TEARING()
230 algorithm
231
2/4
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 34 times.
34 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
232 ✗ print("\nTearing type: minimal\n");
233 end if;
234 34 ocomp := minimalTearing(isyst, ishared, eindex, vindx, jacType, mixedSystem);
235 if debug then execStat("Tearing.minimalTearing"); end if;
236 34 then (ocomp, true);
237
238 case USER_DEFINED_TEARING()
239 algorithm
240 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
241 ✗ print("\nTearing type: user defined\n");
242 ✗ print("Tearing strictness: " + Flags.getConfigString(Flags.TEARING_STRICTNESS) + "\n");
243 end if;
244 ✗ (ocomp,outRunMatching) := userDefinedTearing(isyst, ishared, eindex, vindx, ojac, jacType, mixedSystem, userTVars, userResiduals);
245 if debug then execStat("Tearing.userDefinedTearing"); end if;
246 then (ocomp,outRunMatching);
247 end match;
248 end callTearingMethod;
249
250 protected function tearingSystemWork "author: Frenkel TUD 2012-05"
251 input TearingMethod tearingMethod;
252 input BackendDAE.EqSystem isyst;
253 input BackendDAE.Shared inShared;
254 input Integer inStrongComponentIndex;
255 output BackendDAE.EqSystem osyst;
256 output BackendDAE.Shared outShared = inShared "unused";
257 output Integer outStrongComponentIndex;
258 protected
259 BackendDAE.StrongComponents comps;
260 Boolean runMatching;
261 array<Integer> ass1, ass2;
262 algorithm
263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53245 times.
53245 BackendDAE.EQSYSTEM(matching=BackendDAE.MATCHING(ass1=ass1, ass2=ass2, comps=comps)) := isyst;
264
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 53245 times.
53245 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
265 ✗ print("\n" + BORDER + "\nBEGINNING of traverseComponents\n\n");
266 end if;
267
268 53245 (comps, runMatching, outStrongComponentIndex) := traverseComponents(comps, isyst, inShared, tearingMethod, inStrongComponentIndex);
269
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 53245 times.
53245 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
270 ✗ print("\nEND of traverseComponents\n" + BORDER + "\n\n");
271 end if;
272
2/2
✓ Branch 0 taken 716 times.
✓ Branch 1 taken 52529 times.
53245 osyst := if runMatching then BackendDAEUtil.setEqSystMatching(isyst, BackendDAE.MATCHING(ass1, ass2, comps)) else isyst;
273 end tearingSystemWork;
274
275 protected function traverseComponents "author: Frenkel TUD 2012-05"
276 input BackendDAE.StrongComponents inComps;
277 input BackendDAE.EqSystem isyst;
278 input BackendDAE.Shared ishared;
279 input TearingMethod inMethod;
280 input Integer strongComponentIndexIn;
281 output BackendDAE.StrongComponents oComps;
282 output Boolean outRunMatching = false;
283 output Integer strongComponentIndexOut = strongComponentIndexIn;
284 algorithm
285
4/4
✓ Branch 0 taken 160352 times.
✓ Branch 1 taken 53245 times.
✓ Branch 2 taken 160352 times.
✓ Branch 3 taken 53245 times.
213597 oComps := list(match co
286 local
287 BackendDAE.StrongComponent comp;
288 Boolean b;
289 case comp
290 algorithm
291 160352 (comp, b, strongComponentIndexOut) := traverseComponent(comp, isyst, ishared, inMethod, strongComponentIndexOut);
292
4/4
✓ Branch 0 taken 125499 times.
✓ Branch 1 taken 34853 times.
✓ Branch 2 taken 124783 times.
✓ Branch 3 taken 716 times.
160352 outRunMatching := outRunMatching or b;
293 then comp;
294 end match for co in inComps);
295 end traverseComponents;
296
297 protected function traverseComponent "author: Frenkel TUD 2012-05"
298 input BackendDAE.StrongComponent inComp;
299 input BackendDAE.EqSystem isyst;
300 input BackendDAE.Shared ishared;
301 input TearingMethod inMethod;
302 input Integer strongComponentIndexIn;
303 output BackendDAE.StrongComponent oComp;
304 output Boolean outRunMatching;
305 output Integer strongComponentIndexOut = strongComponentIndexIn;
306 protected
307 constant Boolean debug = false;
308 Boolean debugFlag = Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE);
309 algorithm
310 strongComponentIndexOut := match inComp
311 case BackendDAE.EQUATIONSYSTEM(jac=BackendDAE.FULL_JACOBIAN()) algorithm
312
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2038 times.
2040 if debugFlag then
313 2 print("Handle strong component with index: " + intString(strongComponentIndexOut+1) + "\n");
314
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if not listMember(strongComponentIndexOut+1, Flags.getConfigIntList(Flags.NO_TEARING_FOR_COMPONENT)) then
315 2 print("To disable tearing of this component use '--noTearingForComponent=" + intString(strongComponentIndexOut+1) + "'.\n");
316 end if;
317 end if;
318 2040 then (strongComponentIndexOut + 1);
319 else strongComponentIndexOut;
320 end match;
321
322 (oComp, outRunMatching) := match inComp
323 local
324 list<Integer> eindex, vindx;
325 Option<list<tuple<Integer, Integer, BackendDAE.Equation>>> ojac;
326 BackendDAE.JacobianType jacType;
327 Boolean mixedSystem;
328 Boolean isLinear, useTearing;
329
330 // Tearing
331 case BackendDAE.EQUATIONSYSTEM(eqns=eindex, vars=vindx, jac=BackendDAE.FULL_JACOBIAN(ojac), jacType=jacType, mixedSystem=mixedSystem) algorithm
332 2040 isLinear := BackendDAEUtil.getLinearfromJacType(jacType);
333 2040 useTearing := checkTearingSettings(isLinear, strongComponentIndexOut, listLength(vindx));
334
2/2
✓ Branch 0 taken 2026 times.
✓ Branch 1 taken 14 times.
2040 if useTearing then
335 // do some printing
336
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2024 times.
2026 if debugFlag then
337
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 print("\nTearing of " + (if isLinear then "LINEAR" else "NONLINEAR") + " component\n");
338 () := match (Flags.isSet(Flags.TEARING_DUMPVERBOSE), Flags.isSet(Flags.ITERATION_VARS))
339 case (false, false) algorithm
340 2 print("Use Flag '-d=tearingdumpV' and '-d=iterationVars' for more details\n\n");
341 then ();
342 case (false, true) algorithm
343 ✗ print("Use Flag '-d=tearingdumpV' for more details\n\n");
344 then ();
345 case (true, false) algorithm
346 ✗ print("Use Flag '-d=iterationVars' for more details\n\n");
347 then ();
348 case (true, true) algorithm
349 ✗ print("\n");
350 then ();
351 end match;
352 end if;
353
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2026 times.
2026 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
354 ✗ print("Jacobian:\n" + BackendDump.dumpJacobianStr(ojac) + "\n\n");
355 end if;
356 if debug then
357 execStat("Tearing.traverseComponent " + (if isLinear then "LS" else "NLS") + " start");
358 end if;
359
360 // try the actual tearing
361 try
362 2026 oComp := callTearingMethod(inMethod, isyst, ishared, eindex, vindx, ojac, jacType, mixedSystem, strongComponentIndexOut);
363 outRunMatching := true;
364
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1836 times.
1844 if not tearingPaysOff(oComp, ishared, eindex, vindx, ojac, jacType, strongComponentIndexOut, isLinear) then
365 oComp := inComp;
366 outRunMatching := false;
367 end if;
368 else
369 oComp := inComp;
370 outRunMatching := false;
371 end try;
372 else
373 oComp := inComp;
374 outRunMatching := false;
375 end if;
376 then (oComp, outRunMatching);
377
378 // no component for tearing
379 else (inComp, false);
380 end match;
381 end traverseComponent;
382
383
384 protected function checkTearingSettings
385 "Checks if we want to do tearing for the current component.
386 It will also issue optional maesages if not."
387 input Boolean isLinear;
388 input Integer strongComponentIndex;
389 input Integer numVars;
390 output Boolean activateTearing = false;
391 protected
392 Boolean debugFlag = Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE);
393 Integer maxSize;
394 Boolean isDense;
395 Boolean hasSparseSolver;
396 Boolean forcedTearing;
397 algorithm
398
2/2
✓ Branch 0 taken 916 times.
✓ Branch 1 taken 1124 times.
2956 maxSize := Flags.getConfigInt(if isLinear then Flags.MAX_SIZE_LINEAR_TEARING else Flags.MAX_SIZE_NONLINEAR_TEARING);
399 // Check if tearing is disabled (maxSize=0)
400
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2026 times.
2040 if maxSize == 0 then
401 14 return;
402 end if;
403
404 // Check if (component is too big) or (matrix is dense and target has no sparse solver)
405
2/4
✓ Branch 1 taken 2026 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2026 times.
2026 isDense := Flags.getConfigString(Flags.MATRIX_FORMAT) == "dense";
406 2026 hasSparseSolver := targetHasSparseSolver(isLinear);
407 2026 forcedTearing := isDense and not hasSparseSolver;
408
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2026 times.
2026 if numVars > maxSize and not forcedTearing then
409 ✗ Error.addMessage(Error.MAX_TEARING_SIZE, {intString(strongComponentIndex), intString(numVars),
410 (if isLinear then "linear" else "nonlinear"), intString(maxSize),
411 (if isLinear then "maxSizeLinearTearing" else "maxSizeNonlinearTearing")});
412 ✗ return;
413 end if;
414
415 // Check if tearing is disabled for this component
416
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2026 times.
2026 if listMember(strongComponentIndex, Flags.getConfigIntList(Flags.NO_TEARING_FOR_COMPONENT)) then
417 ✗ if debugFlag then
418 ✗ print("\nTearing deactivated by user.\n");
419 end if;
420 ✗ Error.addMessage(Error.NO_TEARING_FOR_COMPONENT, {intString(strongComponentIndex)});
421 ✗ return;
422 end if;
423
424 activateTearing := true;
425 end checkTearingSettings;
426
427 protected function targetHasSparseSolver
428 input Boolean isLinear;
429 output Boolean hasSparseSolver = listMember(Config.simCodeTarget(), if isLinear then withLSS else withNSS);
430 end targetHasSparseSolver;
431
432 protected function tearingPaysOff
433 "Keeps the torn system unless solving it untorn is clearly cheaper, or unless
434 substituting through the inner equations amplifies an error past what a double
435 carries. The torn matrix does not exist yet, so both its structure and that
436 amplification come from walking the substitution. Costs are in flops."
437 input BackendDAE.StrongComponent tornComp;
438 input BackendDAE.Shared shared;
439 input list<Integer> eindex;
440 input list<Integer> vindx;
441 input Option<list<tuple<Integer, Integer, BackendDAE.Equation>>> ojac;
442 input BackendDAE.JacobianType jacType;
443 input Integer strongComponentIndex;
444 input Boolean isLinear;
445 output Boolean keep = true;
446 protected
447 constant Real entryCost = 4.0 "flops one Jacobian entry evaluation is worth";
448 constant Real fillFactor = 14.0 "fill-in a sparse factorization sees, measured";
449 constant Real precisionDigits = 16.0 "digits a double carries";
450 constant Real trustedCoeffs = 0.9 "known coefficients the growth estimate needs";
451 list<tuple<Integer, Integer, BackendDAE.Equation>> jac;
452 list<Integer> tvars, residuals, innerVars;
453 list<tuple<Integer, Real>> stack;
454 tuple<Integer, Real> dep;
455 BackendDAE.Equation dEqn;
456 BackendDAE.InnerEquations innerEquations;
457 BackendDAE.Variables globalKnownVars;
458 array<list<tuple<Integer, Real>>> eqDeps "local equation index -> the local variables it depends on and their coefficients";
459 array<Integer> tvarId, solvedBy, stamp, depth;
460 array<Real> growth "error amplification of the forward substitution, iteration variables being 1";
461 Integer n, nnz, t, m, row, col, v, w, eq, cnt, nnzA, maxRowA, maxDepth, d, valued = 0;
462 Real density, densityA, buildTorn, costDense, costSparse, costTornDense, costTornSparse;
463 Real costTorn, costUntorn, rn, rt, g, coeff, pivot, maxGrowth, digitsLost;
464 Boolean known, isValued;
465 algorithm
466 (jac, tvars, residuals, innerEquations, known) := match (ojac, tornComp)
467 case (SOME(jac), BackendDAE.TORNSYSTEM(strictTearingSet = BackendDAE.TEARINGSET(
468 tearingvars = tvars, residualequations = residuals, innerEquations = innerEquations)))
469 then (jac, tvars, residuals, innerEquations, true);
470 else ({}, {}, {}, {}, false);
471 end match;
472
2/2
✓ Branch 0 taken 663 times.
✓ Branch 1 taken 1181 times.
1844 if not known then
473 663 return;
474 end if;
475
476 1181 n := listLength(vindx);
477 1181 nnz := listLength(jac);
478 1181 t := listLength(tvars);
479 1181 m := listLength(innerEquations);
480
481 1181 globalKnownVars := BackendDAEUtil.getGlobalKnownVarsFromShared(shared);
482 1181 eqDeps := arrayCreate(n, {});
483
2/2
✓ Branch 0 taken 50793 times.
✓ Branch 1 taken 1181 times.
51974 for entry in jac loop
484 50793 (row, col, dEqn) := entry;
485 50793 (coeff, isValued) := jacEntryMagnitude(dEqn, globalKnownVars);
486
2/2
✓ Branch 0 taken 14970 times.
✓ Branch 1 taken 35823 times.
50793 valued := valued + (if isValued then 1 else 0);
487 101586 arrayUpdate(eqDeps, row, (col, coeff) :: eqDeps[row]);
488 end for;
489
490 // inner equations come in solve order, so one pass gives every depth and growth
491 1181 tvarId := arrayCreate(n, 0);
492 1181 growth := arrayCreate(n, 0.0);
493 cnt := 0;
494
2/2
✓ Branch 0 taken 4888 times.
✓ Branch 1 taken 1181 times.
6069 for gv in tvars loop
495 4888 cnt := cnt + 1;
496 4888 v := List.position(gv, vindx);
497 4888 arrayUpdate(tvarId, v, cnt);
498 4888 arrayUpdate(growth, v, 1.0);
499 end for;
500
501 1181 solvedBy := arrayCreate(n, 0);
502 1181 depth := arrayCreate(n, 0);
503 maxDepth := 0;
504
2/2
✓ Branch 0 taken 12553 times.
✓ Branch 1 taken 1181 times.
13734 for ie in innerEquations loop
505 (eq, innerVars) := match ie
506 case BackendDAE.INNEREQUATION()
507
4/4
✓ Branch 0 taken 12553 times.
✓ Branch 1 taken 12553 times.
✓ Branch 2 taken 12553 times.
✓ Branch 3 taken 12553 times.
25106 then (List.position(ie.eqn, eindex), list(List.position(gv, vindx) for gv in ie.vars));
508 case BackendDAE.INNEREQUATIONCONSTRAINTS()
509 ✗ then (List.position(ie.eqn, eindex), list(List.position(gv, vindx) for gv in ie.vars));
510 end match;
511 d := 0;
512 g := 0.0;
513 pivot := 0.0;
514
2/2
✓ Branch 1 taken 35162 times.
✓ Branch 2 taken 12553 times.
47715 for dep in eqDeps[eq] loop
515 35162 w := Util.tuple21(dep);
516 35162 coeff := Util.tuple22(dep);
517
2/2
✓ Branch 1 taken 12553 times.
✓ Branch 2 taken 22609 times.
35162 if listMember(w, innerVars) then
518 12553 pivot := realMax(pivot, coeff);
519 else
520 22609 d := intMax(d, depth[w]);
521 22609 g := g + coeff * growth[w];
522 end if;
523 end for;
524
2/4
✓ Branch 0 taken 12553 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12553 times.
12553 g := if pivot > 0.0 then g / pivot else g;
525
2/2
✓ Branch 1 taken 12553 times.
✓ Branch 2 taken 12553 times.
25106 for v in innerVars loop
526 12553 arrayUpdate(solvedBy, v, eq);
527 12553 arrayUpdate(depth, v, d + 1);
528
1/2
✓ Branch 0 taken 12553 times.
✗ Branch 1 not taken.
25106 arrayUpdate(growth, v, if g > 1e300 then 1e300 else g);
529 end for;
530 12553 maxDepth := intMax(maxDepth, d + 1);
531 end for;
532
533 1181 stamp := arrayCreate(n, 0);
534 nnzA := 0;
535 maxRowA := 0;
536 maxGrowth := 0.0;
537 row := 0;
538
2/2
✓ Branch 0 taken 4888 times.
✓ Branch 1 taken 1181 times.
6069 for ge in residuals loop
539 4888 row := row + 1;
540 cnt := 0;
541 4888 eq := List.position(ge, eindex);
542 g := 0.0;
543 pivot := 0.0;
544
2/2
✓ Branch 1 taken 15631 times.
✓ Branch 2 taken 4888 times.
20519 for dep in eqDeps[eq] loop
545 15631 w := Util.tuple21(dep);
546 15631 coeff := Util.tuple22(dep);
547 15631 g := g + coeff * growth[w];
548 15631 pivot := realMax(pivot, coeff);
549 end for;
550
2/4
✓ Branch 0 taken 4888 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4888 times.
4888 maxGrowth := realMax(maxGrowth, if pivot > 0.0 then g / pivot else g);
551 4888 stack := eqDeps[eq];
552
2/2
✓ Branch 0 taken 209828 times.
✓ Branch 1 taken 4888 times.
214716 while not listEmpty(stack) loop
553 209828 v := Util.tuple21(listHead(stack));
554 209828 stack := listRest(stack);
555
2/2
✓ Branch 1 taken 129321 times.
✓ Branch 2 taken 80507 times.
209828 if stamp[v] <> row then
556 80507 arrayUpdate(stamp, v, row);
557
2/2
✓ Branch 1 taken 21394 times.
✓ Branch 2 taken 59113 times.
80507 if tvarId[v] > 0 then
558 21394 cnt := cnt + 1;
559 elseif solvedBy[v] > 0 then
560 59113 stack := listAppend(eqDeps[solvedBy[v]], stack);
561 end if;
562 end if;
563 end while;
564 4888 nnzA := nnzA + cnt;
565 maxRowA := intMax(maxRowA, cnt);
566 end for;
567
568 1181 rn := intReal(n);
569 1181 rt := intReal(t);
570
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1181 times.
1181 density := intReal(nnz) / (rn * rn);
571
2/4
✓ Branch 0 taken 1181 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1181 times.
1181 densityA := if t > 0 then intReal(nnzA) / (rt * rt) else 1.0;
572
3/4
✓ Branch 0 taken 1087 times.
✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1087 times.
1181 digitsLost := if maxGrowth > 1.0 then log10(maxGrowth) else 0.0;
573
574 1181 buildTorn := entryCost * intReal(maxRowA * nnz);
575 1181 costDense := entryCost * intReal(nnz) + solveCost(rn, intReal(nnz), false);
576 1181 costSparse := entryCost * intReal(nnz) + solveCost(rn, intReal(nnz), true, fillFactor);
577 1181 costTornDense := buildTorn + solveCost(rt, intReal(nnzA), false);
578 1181 costTornSparse := buildTorn + solveCost(rt, intReal(nnzA), true);
579
580 // costed at the format the backend will emit for each
581
2/2
✓ Branch 1 taken 916 times.
✓ Branch 2 taken 265 times.
1181 costUntorn := if SimCodeCodegenUtil.useSparseSolver(n, nnz, isLinear) then costSparse else costDense;
582
2/2
✓ Branch 1 taken 1165 times.
✓ Branch 2 taken 16 times.
1181 costTorn := if SimCodeCodegenUtil.useSparseSolver(t, nnzA, isLinear) then costTornSparse else costTornDense;
583
584
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1181 times.
1181 if Flags.isSet(Flags.TEARING_COST) then
585 ✗ print("[tearingCost] component " + intString(strongComponentIndex) + " " + (if isLinear then "LS" else "NLS")
586 + " [" + BackendDump.jacobianTypeStr(jacType) + "]"
587 + " n=" + intString(n) + " nnz=" + intString(nnz) + " density=" + realString(density)
588 + " t=" + intString(t) + " inner=" + intString(m) + " nnzA=" + intString(nnzA)
589 + " densityA=" + realString(densityA) + " colors=" + intString(maxRowA) + " chain=" + intString(maxDepth)
590 + " digitsLost=" + realString(digitsLost) + " valuedCoeffs=" + intString(valued) + "/" + intString(nnz)
591 + " cost dense=" + realString(costDense) + " sparse=" + realString(costSparse)
592 + " tornDense=" + realString(costTornDense) + " tornSparse=" + realString(costTornSparse)
593 + " format=" + (if SimCodeCodegenUtil.useSparseSolver(n, nnz, isLinear) then "sparse" else "dense")
594 + " tornFormat=" + (if SimCodeCodegenUtil.useSparseSolver(t, nnzA, isLinear) then "sparse" else "dense") + "\n");
595 end if;
596
597 // a nonlinear system's cost is its Newton iterations, which this does not model
598
2/2
✓ Branch 0 taken 194 times.
✓ Branch 1 taken 987 times.
1181 if not isLinear then
599 194 return;
600 end if;
601
602
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 985 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
987 if digitsLost >= precisionDigits and intReal(valued) >= trustedCoeffs * intReal(nnz) then
603 ✗ Error.addMessage(Error.TEARING_AMPLIFIES_ERROR, {intString(strongComponentIndex), intString(m),
604 intString(realInt(digitsLost))});
605 ✗ keep := false;
606 elseif costUntorn * Flags.getConfigReal(Flags.TEARING_COST_MARGIN) < costTorn then
607 48 Error.addMessage(Error.TEARING_NOT_WORTH_IT, {intString(strongComponentIndex), intString(t),
608 intString(realInt(costTorn)), intString(realInt(costUntorn)),
609 intString(n)});
610 keep := false;
611 end if;
612 end tearingPaysOff;
613
614 protected function jacEntryMagnitude
615 "Magnitude of one Jacobian entry, parameters taken at their bound value. Entries
616 that are not a number at compile time count as 1."
617 input BackendDAE.Equation dEqn;
618 input BackendDAE.Variables globalKnownVars;
619 output Real magnitude;
620 output Boolean known;
621 protected
622 DAE.Exp exp;
623 algorithm
624 (magnitude, known) := matchcontinue dEqn
625 case BackendDAE.RESIDUAL_EQUATION() algorithm
626 50793 exp := dEqn.exp;
627 51061 for i in 1:3 loop
628
2/2
✓ Branch 0 taken 51061 times.
✓ Branch 1 taken 35240 times.
86301 if Expression.isScalarConst(exp) then
629 break;
630 end if;
631 51061 (exp, _) := Expression.traverseExpBottomUp(exp, substituteKnownVar, globalKnownVars);
632 51061 (exp, _) := ExpressionSimplify.simplify(exp);
633 end for;
634 50793 then (abs(Expression.toReal(exp)), true);
635 else (1.0, false);
636 end matchcontinue;
637 end jacEntryMagnitude;
638
639 protected function substituteKnownVar
640 input output DAE.Exp exp;
641 input output BackendDAE.Variables globalKnownVars;
642 algorithm
643 exp := matchcontinue exp
644 local
645 BackendDAE.Var var;
646 case DAE.CREF() algorithm
647 90582 (var, _) := BackendVariable.getVarSingle(exp.componentRef, globalKnownVars);
648 31085 then BackendVariable.varBindExp(var);
649 295958 else exp;
650 end matchcontinue;
651 end substituteKnownVar;
652
653 protected function solveCost
654 "One factorization and back-solve, sparse LU modelled as nnz/size per column."
655 input Real size;
656 input Real nnz;
657 input Boolean sparse;
658 input Real fill = 1.0 "fill-in the factorization sees, 1 for an already dense matrix";
659 output Real cost;
660 algorithm
661
4/6
✓ Branch 0 taken 4724 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2362 times.
✓ Branch 3 taken 2362 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2362 times.
4724 cost := if size <= 0.0 then 0.0
662 elseif sparse then fill * nnz * nnz / size + 2.0 * nnz
663 else 2.0 / 3.0 * size * size * size + 2.0 * size * size;
664 end solveCost;
665
666 protected function getUserTearingSet
667 input list<Integer> userTVars;
668 input list<Integer> userResiduals;
669 input Integer strongComponentIndex;
670 output list<Integer> userTvarsThisComponent = {};
671 output list<Integer> userResidualsThisComponent = {};
672 protected
673 Integer i, len, start, end_;
674 array<Integer> arr_TVars, arr_residuals;
675 algorithm
676 // work with arrays so the accesses are faster
677 2025 arr_TVars := listArray(userTVars);
678 2025 arr_residuals := listArray(userResiduals);
679
680 // find tearing variables
681 i := 1;
682 2025 len := listLength(userTVars);
683
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2024 times.
2026 while i < len loop
684
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
2 if arr_TVars[i] == strongComponentIndex then
685 start := i + 2;
686 1 end_ := i + 1 + arr_TVars[i+1];
687
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 userTvarsThisComponent := List.unique(list(arr_TVars[j] for j in start:end_));
688
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 if listLength(userTvarsThisComponent) <> arr_TVars[i+1] then
689 ✗ Error.addMessage(Error.USER_DEFINED_TEARING_ERROR, {"The selected tearing variables must have unique indexes."});
690 ✗ fail();
691 end if;
692 break;
693 else
694 1 i := i + 2 + arr_TVars[i+1];
695 end if;
696 end while;
697
698 // find residual equations
699
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2024 times.
2025 if not listEmpty(userTvarsThisComponent) then
700 i := 1;
701 1 len := listLength(userResiduals);
702
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 while i < len loop
703 ✗ if arr_residuals[i] == strongComponentIndex then
704 start := i + 2;
705 ✗ end_ := i + 1 + arr_residuals[i+1];
706 ✗ userResidualsThisComponent := List.unique(list(arr_residuals[j] for j in start:end_));
707 ✗ if listLength(userResidualsThisComponent) <> arr_residuals[i+1] then
708 ✗ Error.addMessage(Error.USER_DEFINED_TEARING_ERROR, {"The selected residual equations must have unique indexes."});
709 ✗ fail();
710 end if;
711 break;
712 else
713 ✗ i := i + 2 + arr_residuals[i+1];
714 end if;
715 end while;
716 end if;
717 end getUserTearingSet;
718
719
720 // =============================================================================
721 //
722 // method: omc tearing
723 //
724 // =============================================================================
725
726 protected function omcTearing " author: Frenkel TUD 2012-05"
727 input BackendDAE.EqSystem isyst;
728 input BackendDAE.Shared ishared;
729 input list<Integer> eindex;
730 input list<Integer> vindx;
731 input Option<list<tuple<Integer, Integer, BackendDAE.Equation>>> ojac;
732 input BackendDAE.JacobianType jacType;
733 input Boolean mixedSystem;
734 output BackendDAE.StrongComponent ocomp;
735 output Boolean outRunMatching;
736 protected
737 list<Integer> tvars,residual,unsolvables;
738 list<list<Integer>> othercomps;
739 BackendDAE.EqSystem subsyst;
740 array<Integer> ass1,ass2,columark;
741 Integer size,tornsize,mark;
742 list<BackendDAE.Equation> eqn_lst;
743 list<BackendDAE.Var> var_lst;
744 BackendDAE.Variables vars;
745 BackendDAE.EquationArray eqns;
746 BackendDAE.AdjacencyMatrix m,m1;
747 BackendDAE.AdjacencyMatrix mt,mt1;
748 BackendDAE.AdjacencyMatrixEnhanced me;
749 BackendDAE.AdjacencyMatrixTEnhanced meT;
750 array<list<Integer>> mapEqnIncRow;
751 array<Integer> mapIncRowEqn;
752 AvlTreePathFunction.Tree funcs;
753 list<Integer> tSel_always, tSel_prefer, tSel_avoid, tSel_never;
754 String DAEtypeStr;
755 algorithm
756
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
35 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
757 ✗ print("\n" + BORDER + "\nBEGINNING of omcTearing\n\n");
758 end if;
759 35 DAEtypeStr := BackendDump.printBackendDAEType2String(ishared.backendDAEType);
760 // generate Subsystem to get the adjacency matrix
761 35 size := listLength(vindx);
762 35 eqn_lst := BackendEquation.getList(eindex,BackendEquation.getEqnsFromEqSystem(isyst));
763 35 eqns := BackendEquation.listEquation(eqn_lst);
764 35 var_lst := List.map1r(vindx, BackendVariable.getVarAt, BackendVariable.daeVars(isyst));
765 35 vars := BackendVariable.listVar1(var_lst);
766 35 subsyst := BackendDAEUtil.createEqSystem(vars, eqns);
767 35 funcs := BackendDAEUtil.getFunctions(ishared);
768 70 (subsyst,m,mt,_,_) := BackendDAEUtil.getAdjacencyMatrixScalar(subsyst, BackendDAE.NORMAL(), SOME(funcs), BackendDAEUtil.isInitializationDAE(ishared));
769 // DumpGraphML.dumpSystem(subsyst,ishared,NONE(),"System" + intString(size) + ".graphml");
770
2/4
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35 times.
35 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
771 ✗ print("\n\n###BEGIN print Strong Component#####################\n(Function:omcTearing)\n");
772 ✗ BackendDump.printEqSystem(subsyst);
773 ✗ print("\n###END print Strong Component#######################\n(Function:omcTearing)\n\n\n");
774 end if;
775 35 (me,meT,mapEqnIncRow,mapIncRowEqn) := BackendDAEUtil.getAdjacencyMatrixEnhancedScalar(subsyst,ishared,false);
776
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
35 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
777 ✗ print("\n\nAdjacencyMatrixEnhanced:\n");
778 ✗ BackendDump.dumpAdjacencyMatrixEnhanced(me);
779 ✗ print("\nAdjacencyMatrixTransposedEnhanced:\n");
780 ✗ BackendDump.dumpAdjacencyMatrixTEnhanced(meT);
781 ✗ print("\nmapEqnIncRow:"); //+ stringDelimitList(List.map(List.flatten(arrayList(mapEqnIncRow)),intString),",") + "\n\n");
782 ✗ BackendDump.dumpAdjacencyMatrix(mapEqnIncRow);
783 ✗ print("\nmapIncRowEqn:\n" + stringDelimitList(List.mapArray(mapIncRowEqn,intString),",") + "\n\n");
784 end if;
785
786 35 ass1 := arrayCreate(size,-1);
787 35 ass2 := arrayCreate(size,-1);
788 // get all unsolvable variables
789 35 unsolvables := getUnsolvableVars(size,meT);
790
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
35 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
791 ✗ print("\n\nUnsolvable Vars:\n");
792 ✗ BackendDump.debuglst(unsolvables,intString,", ","\n");
793 end if;
794 35 columark := arrayCreate(size,-1);
795
796 // Collect variables with annotation attribute '__OpenModelica_tearingSelect in [always, prefer, avoid, never]'
797 35 (tSel_always,tSel_prefer,tSel_avoid,tSel_never,_) := tearingSelect(var_lst, {}, DAEtypeStr);
798
799 // determine tvars and do cheap matching until a maximum matching is there
800 // if cheap matching stucks select additional tearing variable and continue
801 // (mark+1 for every call of omcTearing3)
802
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
35 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
803 ✗ print("\n" + BORDER + "\nBEGINNING of omcTearing2\n\n");
804 end if;
805 35 (tvars,mark) := omcTearing2(unsolvables,tSel_always,tSel_prefer,tSel_avoid,tSel_never,me,meT,mapEqnIncRow,mapIncRowEqn,size,vars,ishared,ass1,ass2,columark,1,{});
806
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
35 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
807 ✗ print("\nEND of omcTearing2\n" + BORDER + "\n\n");
808 end if;
809
810 // unassign tvars
811 35 ass1 := List.fold(tvars,unassignTVars,ass1);
812
2/4
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35 times.
35 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
813 ✗ print("\n" + BORDER + "\n* BFS RESULTS:\n* ass1: "+ stringDelimitList(List.mapArray(ass1, intString),",") +"\n");
814 ✗ print("* ass2: "+ stringDelimitList(List.mapArray(ass2,intString),",") + "\n" + BORDER +"\n\n");
815 end if;
816
817 // unmatched equations are residual equations
818 35 residual := Matching.getUnassigned(size,ass2,{});
819 // subsyst := BackendDAEUtil.setEqSystMatching(subsyst,BackendDAE.MATCHING(ass1,ass2,{}));
820 // DumpGraphML.dumpSystem(subsyst,ishared,NONE(),"TornSystem" + intString(size) + ".graphml");
821
822 // check if tearing makes sense
823 35 tornsize := listLength(tvars);
824
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 30 times.
35 true := intLt(tornsize, size);
825
826 // create adjacency matrices w/o tvar and residual
827 30 m1 := arrayCreate(size,{});
828 30 mt1 := arrayCreate(size,{});
829 30 m1 := AdjacencyMatrix.getOtherEqSysAdjacencyMatrix(m,size,1,ass2,ass1,m1);
830 30 mt1 := AdjacencyMatrix.getOtherEqSysAdjacencyMatrix(mt,size,1,ass1,ass2,mt1);
831
832 // run tarjan to get order of other equations
833 30 othercomps := Sorting.TarjanTransposed(mt1, ass2);
834
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
835 ✗ print("\nOtherEquationsOrder:\n");
836 ✗ BackendDump.dumpComponentsOLD(othercomps);
837 ✗ print("\n");
838 end if;
839
840 // calculate influence of tearing vars in residual equations
841 // mt1: row=variable, columns: tvars, that influence the result of the variable
842 30 mt1 := arrayCreate(size, {});
843 30 mark := getDependenciesOfVars(othercomps, ass1, ass2, m, mt1, columark, mark);
844
845 30 (residual, mark) := sortResidualDepentOnTVars(residual, tvars, ass1, m, mt1, columark, mark);
846 30 (ocomp,outRunMatching) := omcTearing4(jacType,isyst,ishared,subsyst,tvars,residual,ass1,ass2,othercomps,eindex,vindx,mapEqnIncRow,mapIncRowEqn,columark,mark,mixedSystem);
847
848
2/4
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 30 times.
30 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
849 ✗ print(if outRunMatching then "\nStatus:\nOk system torn\n\n" else "\nStatus:\nSystem not torn\n\n");
850 ✗ print("\n" + BORDER + "\n* TEARING RESULTS:\n*\n* No of equations in strong component: "+intString(size)+"\n");
851 ✗ print("* No of tVars: "+intString(tornsize)+"\n");
852 ✗ print("*\n* tVars: "+ stringDelimitList(List.map(tvars,intString),",") + "\n");
853 ✗ print("*\n* resEq: "+ stringDelimitList(List.map(residual,intString),",") + "\n*\n*");
854 ✗ BackendDAE.TORNSYSTEM(BackendDAE.TEARINGSET(tearingvars=tvars,residualequations=residual)) := ocomp;
855 ✗ print("\n* Related to entire Equationsystem:\n* =====\n* tVars: "+ stringDelimitList(List.map(tvars,intString),",") + "\n* =====\n");
856 ✗ print("*\n* =====\n* resEq: "+ stringDelimitList(List.map(residual,intString),",") + "\n* =====\n" + BORDER + "\n");
857 end if;
858
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
859 ✗ print("\n\nStrongComponents:\n");
860 ✗ BackendDump.dumpComponent(ocomp);
861 ✗ print("\n\nEND of omcTearing\n" + BORDER + "\n\n");
862 end if;
863 end omcTearing;
864
865
866 protected function getUnsolvableVars
867 " author: Frenkel TUD 2012-08"
868 input Integer size;
869 input BackendDAE.AdjacencyMatrixTEnhanced meT;
870 output list<Integer> unsolvables = {};
871 protected
872 Boolean isUnsolvable;
873 algorithm
874
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1995 times.
38921 for index in 1:size loop
875 36926 isUnsolvable := unsolvable(meT[index]);
876
2/2
✓ Branch 0 taken 2025 times.
✓ Branch 1 taken 34901 times.
36926 if isUnsolvable then
877 unsolvables := index::unsolvables;
878 end if;
879 end for;
880 end getUnsolvableVars;
881
882
883 public function unsolvable
884 " author: Frenkel TUD 2012-08"
885 input BackendDAE.AdjacencyMatrixElementEnhanced elem;
886 output Boolean isUnsolvable = true;
887 protected
888 Integer e;
889 BackendDAE.Solvability s;
890 algorithm
891
2/2
✓ Branch 0 taken 860496 times.
✓ Branch 1 taken 2025 times.
862521 for el in elem loop
892 860496 (e,s,_) := el;
893
2/2
✓ Branch 1 taken 456484 times.
✓ Branch 2 taken 404012 times.
860496 if solvable(s) then
894
2/2
✓ Branch 0 taken 456480 times.
✓ Branch 1 taken 4 times.
456484 if e > 0 then
895 isUnsolvable := false;
896 456480 return;
897 end if;
898 end if;
899 end for;
900 end unsolvable;
901
902
903 protected function unassignTVars " author: Frenkel TUD 2012-05"
904 input Integer v;
905 input array<Integer> inAss;
906 output array<Integer> outAss;
907 algorithm
908 74 outAss := arrayUpdate(inAss,v,-1);
909 end unassignTVars;
910
911
912
913
914 protected function getDependenciesOfVars " function to determine which variables are influenced by the tvars"
915 input list<list<Integer>> iComps;
916 input array<Integer> ass1;
917 input array<Integer> ass2;
918 input BackendDAE.AdjacencyMatrix m;
919 input array<list<Integer>> mT;
920 input array<Integer> visited;
921 input Integer iMark;
922 output Integer oMark;
923 algorithm
924 oMark := match iComps
925 local
926 Integer c, v;
927 list<Integer> comp, tvars, vars;
928 list<list<Integer>> comps;
929
930 case {}
931 then iMark;
932
933 case {c}::comps algorithm
934 // get var of eqn
935 201 v := ass2[c];
936 // get TVars of Eqn
937 201 vars := List.select(m[c], Util.intPositive);
938 201 tvars := tVarsofEqn(vars, ass1, mT, visited, iMark, {});
939 // update map
940 201 arrayUpdate(mT, v, tvars);
941 201 then getDependenciesOfVars(comps, ass1, ass2, m, mT, visited, iMark+1);
942
943 case comp::comps algorithm
944 // get var of eqns
945 2 vars := List.map1r(comp,arrayGet,ass2);
946 // get TVars of Eqns
947 2 tvars := tVarsofEqns(comp, m, ass1, mT, visited, iMark);
948 // update map
949 2 List.fold1r(vars, arrayUpdate, tvars, mT);
950 2 then getDependenciesOfVars(comps, ass1, ass2, m, mT, visited, iMark+1);
951 end match;
952 end getDependenciesOfVars;
953
954
955 protected function tVarsofEqns "determines tvars that influence this equations"
956 input list<Integer> iEqns;
957 input BackendDAE.AdjacencyMatrix m;
958 input array<Integer> ass1;
959 input array<list<Integer>> mT;
960 input array<Integer> visited;
961 input Integer iMark;
962 output list<Integer> oAcc = {};
963 protected
964 list<Integer> vars;
965 algorithm
966
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 for e in iEqns loop
967 6 vars := List.select(m[e], Util.intPositive);
968 6 oAcc := tVarsofEqn(vars, ass1, mT, visited, iMark, oAcc);
969 end for;
970 end tVarsofEqns;
971
972
973 protected function tVarsofEqn "determines tvars that influence this equation"
974 input list<Integer> iVars;
975 input array<Integer> ass1;
976 input array<list<Integer>> mT;
977 input array<Integer> visited;
978 input Integer iMark;
979 input list<Integer> iAcc;
980 output list<Integer> oAcc = iAcc;
981 algorithm
982
2/2
✓ Branch 0 taken 686 times.
✓ Branch 1 taken 271 times.
957 for v in iVars loop
983
2/2
✓ Branch 1 taken 187 times.
✓ Branch 2 taken 499 times.
686 if intLt(ass1[v],0) then
984 187 oAcc := uniqueIntLst(v,iMark,visited,oAcc);
985 else
986 499 oAcc := List.fold2(mT[v],uniqueIntLst,iMark,visited,oAcc);
987 end if;
988 end for;
989 end tVarsofEqn;
990
991
992 protected function uniqueIntLst
993 input Integer c;
994 input Integer mark;
995 input array<Integer> markarray;
996 input list<Integer> iAcc;
997 output list<Integer> oAcc = iAcc;
998 algorithm
999
2/2
✓ Branch 1 taken 156 times.
✓ Branch 2 taken 536 times.
692 if not intEq(mark,markarray[c]) then
1000 536 arrayUpdate(markarray,c,mark);
1001 oAcc := c::oAcc;
1002 end if;
1003 end uniqueIntLst;
1004
1005
1006 protected function sortResidualDepentOnTVars
1007 input list<Integer> iResiduals;
1008 input list<Integer> iTVars;
1009 input array<Integer> ass1;
1010 input BackendDAE.AdjacencyMatrix m;
1011 input array<list<Integer>> mT;
1012 input array<Integer> visited;
1013 input Integer iMark;
1014 output list<Integer> oResiduals;
1015 output Integer oMark;
1016 protected
1017 Integer size;
1018 list<list<Integer>> maplst;
1019 array<list<Integer>> map;
1020 array<Integer> eqnLocalGlobal,varGlobalLocal,v1,v2;
1021 algorithm
1022 // eqn - local - Global indices
1023 30 eqnLocalGlobal := listArray(iResiduals);
1024 // var - global local indices
1025 30 varGlobalLocal := arrayCreate(arrayLength(m),-1);
1026 30 varGlobalLocal := getGlobalLocal(iTVars,1,varGlobalLocal);
1027 // generate list of map[residual]=tvars
1028 // change indices in map to local
1029 30 (oMark,maplst) := tVarsofResidualEqns(iResiduals,m,ass1,mT,varGlobalLocal,visited,iMark);
1030 30 map := listArray(maplst);
1031 // get for each residual a tvar
1032 size := arrayLength(map);
1033 30 Matching.matchingExternalsetAdjacencyMatrix(size,size,map);
1034 30 BackendDAEEXT.matching(size,size,5,-1,1.0,1);
1035 30 v1 := arrayCreate(size,-1);
1036 30 v2 := arrayCreate(size,-1);
1037 30 BackendDAEEXT.getAssignment(v2,v1);
1038 // BackendDump.dumpAdjacencyMatrix(map);
1039 // BackendDump.dumpMatching(v1);
1040 // BackendDump.dumpMatching(v2);
1041 // sort residuals depent on matching to tvars
1042 30 oResiduals := getTVarResiduals(size,v1,eqnLocalGlobal,{});
1043 //print("iResiduals " + stringDelimitList(List.map(iResiduals,intString),",") + "\n");
1044 //print("oResiduals " + stringDelimitList(List.map(oResiduals,intString),",") + "\n");
1045 end sortResidualDepentOnTVars;
1046
1047
1048 protected function getGlobalLocal
1049 input list<Integer> iTVars;
1050 input Integer index;
1051 input array<Integer> iVarGlobalLocal;
1052 output array<Integer> oVarGlobalLocal = iVarGlobalLocal;
1053 protected
1054 Integer idx = index;
1055 algorithm
1056
2/2
✓ Branch 1 taken 64 times.
✓ Branch 2 taken 30 times.
94 for i in iTVars loop
1057 64 arrayUpdate(oVarGlobalLocal,i,idx);
1058 64 idx := idx + 1;
1059 end for;
1060 end getGlobalLocal;
1061
1062
1063 protected function tVarsofResidualEqns
1064 input list<Integer> iEqns;
1065 input BackendDAE.AdjacencyMatrix m;
1066 input array<Integer> ass1;
1067 input array<list<Integer>> mT;
1068 input array<Integer> varGlobalLocal;
1069 input array<Integer> visited;
1070 input Integer iMark;
1071 output Integer oMark = iMark;
1072 output list<list<Integer>> oAcc;
1073 algorithm
1074
4/4
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 64 times.
✓ Branch 3 taken 30 times.
94 oAcc := list(
1075 match eq
1076 local
1077 Integer e;
1078 list<Integer> vars,tvars;
1079 case e
1080 algorithm
1081 64 vars := List.select(m[e], Util.intPositive);
1082 64 tvars := tVarsofEqn(vars,ass1,mT,visited,oMark,{});
1083 // change indices to local
1084 64 tvars := List.map1r(tvars,arrayGet,varGlobalLocal);
1085 64 oMark := oMark + 1;
1086 then tvars;
1087 end match for eq in iEqns);
1088 end tVarsofResidualEqns;
1089
1090
1091 protected function getTVarResiduals
1092 input Integer index;
1093 input array<Integer> v1;
1094 input array<Integer> eqnLocalGlobal;
1095 input list<Integer> iAcc;
1096 output list<Integer> oAcc;
1097 algorithm
1098 oAcc := match index
1099 local
1100 Integer e;
1101 case 0 then iAcc;
1102 case _
1103 algorithm
1104 64 e := v1[index];
1105 64 e := eqnLocalGlobal[e];
1106 64 then
1107 getTVarResiduals(index-1,v1,eqnLocalGlobal,e::iAcc);
1108 end match;
1109 end getTVarResiduals;
1110
1111
1112 protected function omcTearing2 " function to determine tvars and do cheap matching
1113 author: Frenkel TUD 2012-05"
1114 input list<Integer> unsolvables;
1115 input list<Integer> tSel_always;
1116 input list<Integer> tSel_prefer;
1117 input list<Integer> tSel_avoid;
1118 input list<Integer> tSel_never;
1119 input BackendDAE.AdjacencyMatrixEnhanced m;
1120 input BackendDAE.AdjacencyMatrixTEnhanced mt;
1121 input array<list<Integer>> mapEqnIncRow;
1122 input array<Integer> mapIncRowEqn;
1123 input Integer size;
1124 input BackendDAE.Variables vars;
1125 input BackendDAE.Shared ishared;
1126 input array<Integer> ass1;
1127 input array<Integer> ass2;
1128 input array<Integer> columark;
1129 input Integer mark;
1130 input list<Integer> inTVars;
1131 output list<Integer> outTVars;
1132 output Integer oMark;
1133 algorithm
1134 (outTVars,oMark) := matchcontinue(unsolvables,tSel_always)
1135 local
1136 Integer tvar;
1137 list<Integer> unassigned,rest,unsolv;
1138 BackendDAE.AdjacencyMatrixElementEnhanced vareqns;
1139 // if there are no unsolvables choose tvar by heuristic
1140 case ({},{})
1141 algorithm
1142 // select tearing var
1143
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1144 ✗ print("\n" + BORDER + "\nBEGINNING of omcTearingSelectTearingVar\n\n\n");
1145 end if;
1146 44 tvar := omcTearingSelectTearingVar(vars,ass1,ass2,m,mt,tSel_prefer,tSel_avoid,tSel_never);
1147
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1148 ✗ print("\nEND of omcTearingSelectTearingVar\n" + BORDER + "\n\n");
1149 end if;
1150 // mark tearing var
1151 44 arrayUpdate(ass1,tvar,size*2);
1152 // equations not yet assigned containing the tvar
1153 44 vareqns := List.removeOnTrue(ass2, isAssignedSaveEnhanced, mt[tvar]);
1154
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1155 ✗ print("Assignable equations containing new tvar:\n");
1156 ✗ BackendDump.dumpAdjacencyRowEnhanced(vareqns);
1157 ✗ print("\n");
1158 end if;
1159 // cheap matching
1160 44 tearingBFS(vareqns,m,mt,mapEqnIncRow,mapIncRowEqn,size,ass1,ass2,{});
1161 // check for unassigned vars, if there some rerun
1162 44 unassigned := Matching.getUnassigned(size,ass1,{});
1163 44 (outTVars,oMark) := omcTearing3(unassigned,{},tSel_always,tSel_prefer,tSel_avoid,tSel_never,m,mt,mapEqnIncRow,mapIncRowEqn,size,vars,ishared,ass1,ass2,columark,mark+1,tvar::inTVars);
1164 then
1165 (outTVars,oMark);
1166 // if there are unsolvables choose unsolvables as tvars
1167 case (tvar::rest,{})
1168 algorithm
1169
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if listMember(tvar,tSel_never) then
1170 ✗ Error.addCompilerWarning("There are tearing variables with annotation attribute '__OpenModelica_tearingSelect = TearingSelect.never'. Use -d=tearingdump and -d=tearingdumpV for more information.");
1171 end if;
1172
2/4
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 30 times.
30 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1173 ✗ print("\nForced selection of Tearing Variable:\n" + UNDERLINE + "\n");
1174 ✗ print("tVar: " + intString(tvar) + " (unsolvable in omcTearing2)\n\n\n");
1175 end if;
1176 // mark tearing var
1177 30 arrayUpdate(ass1,tvar,size*2);
1178 // equations not yet assigned containing the tvar
1179 30 vareqns := List.removeOnTrue(ass2, isAssignedSaveEnhanced, mt[tvar]);
1180
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1181 ✗ print("Assignable equations containing new tvar:\n");
1182 ✗ BackendDump.dumpAdjacencyRowEnhanced(vareqns);
1183 ✗ print("\n");
1184 end if;
1185 // cheap matching
1186 30 tearingBFS(vareqns,m,mt,mapEqnIncRow,mapIncRowEqn,size,ass1,ass2,{});
1187 // check for unassigned vars, if there some rerun
1188 30 unassigned := Matching.getUnassigned(size,ass1,{});
1189 30 (outTVars,oMark) := omcTearing3(unassigned,rest,tSel_always,tSel_prefer,tSel_avoid,tSel_never,m,mt,mapEqnIncRow,mapIncRowEqn,size,vars,ishared,ass1,ass2,columark,mark+1,tvar::inTVars);
1190 then
1191 (outTVars,oMark);
1192 case (_,_)
1193 algorithm
1194 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1195 ✗ print("\nForced selection of Tearing Variables:\n" + UNDERLINE + "\n");
1196 ✗ print("Variables with annotation attribute 'always' as tVars: " + stringDelimitList(List.map(tSel_always,intString),",")+"\n");
1197 end if;
1198 // mark tearing var
1199 ✗ markTVarsOrResiduals(tSel_always, ass1);
1200 ✗ (_,unsolv,_) := List.intersection1OnTrue(unsolvables,tSel_always,intEq);
1201 // equations not yet assigned containing the tvars
1202 ✗ vareqns := findVareqns(ass2,isAssignedSaveEnhanced,mt,tSel_always);
1203 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1204 ✗ print("Assignable equations containing new tvars:\n");
1205 ✗ BackendDump.dumpAdjacencyRowEnhanced(vareqns);
1206 ✗ print("\n");
1207 end if;
1208 // cheap matching
1209 ✗ tearingBFS(vareqns,m,mt,mapEqnIncRow,mapIncRowEqn,size,ass1,ass2,{});
1210 // check for unassigned vars, if there some rerun
1211 ✗ unassigned := Matching.getUnassigned(size,ass1,{});
1212 ✗ (outTVars,oMark) := omcTearing3(unassigned,unsolv,{},tSel_prefer,tSel_avoid,tSel_never,m,mt,mapEqnIncRow,mapIncRowEqn,size,vars,ishared,ass1,ass2,columark,mark+1,listAppend(tSel_always,inTVars));
1213 then
1214 (outTVars,oMark);
1215 else
1216 algorithm
1217 ✗ print("Tearing.omcTearing2 failed!");
1218 ✗ then
1219 fail();
1220 end matchcontinue;
1221 end omcTearing2;
1222
1223
1224 protected function findVareqns
1225 "Function returns equations not yet assigned containing the currently handled tvars.
1226 author: ptaeuber FHB 2014-05"
1227 input array<Integer> ass2In;
1228 input CompFunc inCompFunc;
1229 input BackendDAE.AdjacencyMatrixTEnhanced mt;
1230 input list<Integer> tSel_alwaysIn;
1231 output list<tuple<Integer,BackendDAE.Solvability,BackendDAE.Constraints>> vareqnsOut = {};
1232 partial function CompFunc
1233 input array<Integer> inValue;
1234 input tuple<Integer,BackendDAE.Solvability,BackendDAE.Constraints> inElement;
1235 output Boolean outIsEqual;
1236 end CompFunc;
1237 algorithm
1238 ✗ for tvar in tSel_alwaysIn loop
1239 ✗ vareqnsOut := List.append_reverse(List.removeOnTrue(ass2In,inCompFunc,mt[tvar]), vareqnsOut);
1240 end for;
1241 ✗ vareqnsOut := List.unique(vareqnsOut);
1242 end findVareqns;
1243
1244
1245 protected function omcTearingSelectTearingVar " author: Frenkel TUD 2012-05"
1246 input BackendDAE.Variables vars;
1247 input array<Integer> ass1;
1248 input array<Integer> ass2;
1249 input BackendDAE.AdjacencyMatrixEnhanced m;
1250 input BackendDAE.AdjacencyMatrixTEnhanced mt;
1251 input list<Integer> tSel_prefer;
1252 input list<Integer> tSel_avoid;
1253 input list<Integer> tSel_never;
1254 output Integer tearingVar;
1255 algorithm
1256 tearingVar := matchcontinue tSel_never
1257 local
1258 list<Integer> freeVars,eqns,unsolvables,pointsLst;
1259 Integer tvar;
1260 Integer varsize;
1261 array<Integer> points;
1262
1263 // if there is a variable unsolvable select it
1264 case _
1265 algorithm
1266 44 unsolvables := getUnsolvableVarsConsiderMatching(BackendVariable.varsSize(vars),mt,ass1,ass2);
1267
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 false := listEmpty(unsolvables);
1268 ✗ tvar := listHead(unsolvables);
1269 ✗ if listMember(tvar,tSel_never) then
1270 ✗ Error.addCompilerWarning("There are tearing variables with annotation attribute '__OpenModelica_tearingSelect = TearingSelect.never'. Use -d=tearingdump and -d=tearingdumpV for more information.");
1271 end if;
1272 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1273 ✗ print("\nForced selection of Tearing Variable:\n" + UNDERLINE + "\n");
1274 end if;
1275 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1276 ✗ print("tVar: " + intString(tvar) + " (unsolvable in omcTearingSelectTearingVar)\n\n");
1277 end if;
1278 then
1279 tvar;
1280
1281 case _
1282 algorithm
1283 44 varsize := BackendVariable.varsSize(vars);
1284 // variables not assigned yet:
1285 44 freeVars := Matching.getUnassigned(varsize,ass1,{});
1286
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1287 ✗ print("omcTearingSelectTearingVar Candidates(unassigned vars):\n");
1288 ✗ BackendDump.debuglst(freeVars,intString,", ","\n");
1289 end if;
1290 44 (_,freeVars,_) := List.intersection1OnTrue(freeVars,tSel_never,intEq);
1291
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1292 ✗ print("Candidates without variables with annotation attribute 'never':\n");
1293 ✗ BackendDump.debuglst(freeVars,intString,", ","\n");
1294 end if;
1295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 false := listEmpty(freeVars);
1296
1297 // CALCULATE TEARING-VARIABLE WEIGHTS
1298 44 points := arrayCreate(varsize,0);
1299 // 1st: Points for solvability (see function solvabilityWeights)
1300 44 points := List.fold2(freeVars, calcVarWeights,mt,ass2,points);
1301
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1302 ✗ print("\nPoints after 'calcVarWeights':\n" + stringDelimitList(List.mapArray(points, intString),",") + "\n\n");
1303 end if;
1304 44 eqns := Matching.getUnassigned(arrayLength(m),ass2,{});
1305 // 2nd: 5 points for each equation this variable would causalize
1306 44 points := List.fold2(eqns,addEqnWeights,m,ass1,points);
1307
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1308 ✗ print("Points after 'addEqnWeights':\n" + stringDelimitList(List.mapArray(points, intString),",") + "\n\n");
1309 end if;
1310 // 3rd: only one-tenth of points for each discrete variable
1311 44 points := List.fold1(freeVars,discriminateDiscrete,vars,points);
1312
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1313 ✗ print("Points after 'discriminateDiscrete':\n" + stringDelimitList(List.mapArray(points, intString),",") + "\n\n");
1314 end if;
1315 // 4th: Prefer variables with annotation attribute '__OpenModelica_tearingSelect = TearingSelect.prefer'
1316 44 pointsLst := preferAvoidVariables(freeVars, arrayList(points), tSel_prefer, 3.0);
1317
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1318 ✗ print("Points after preferring variables with attribute 'prefer':\n" + stringDelimitList(List.map(pointsLst,intString),",") + "\n\n");
1319 end if;
1320 // 5th: Avoid variables with annotation attribute '__OpenModelica_tearingSelect = TearingSelect.avoid'
1321 44 pointsLst := preferAvoidVariables(freeVars, pointsLst, tSel_avoid, 0.334);
1322
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1323 ✗ print("Points after discrimination against variables with attribute 'avoid':\n" + stringDelimitList(List.map(pointsLst,intString),",") + "\n\n");
1324 end if;
1325 44 tvar := selectVarWithMostPoints(freeVars,pointsLst);
1326 // fcall(Flags.TEARING_DUMPVERBOSE,print,"VarsWithMostEqns:\n");
1327 // fcall(Flags.TEARING_DUMPVERBOSE,BackendDump.debuglst,(freeVars,intString,", ","\n"));
1328
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1329 ✗ print("tVar: " + intString(tvar) + " (" + intString(listGet(pointsLst,tvar)) + " points)\n\n");
1330 elseif listMember(tvar,tSel_avoid) then
1331 2 Error.addCompilerWarning("The Tearing heuristic has chosen variables with annotation attribute '__OpenModelica_tearingSelect = TearingSelect.avoid'. Use -d=tearingdump and -d=tearingdumpV for more information.");
1332 end if;
1333 then
1334 tvar;
1335 else
1336 algorithm
1337 ✗ print("omcTearingSelectTearingVar failed because no unmatched var!\n");
1338 ✗ then
1339 fail();
1340 end matchcontinue;
1341 end omcTearingSelectTearingVar;
1342
1343
1344 protected function getUnsolvableVarsConsiderMatching
1345 " returns one unsolvable var with respect to the current matching
1346 author: Frenkel TUD 2012-08"
1347 input Integer size;
1348 input BackendDAE.AdjacencyMatrixTEnhanced meT;
1349 input array<Integer> ass1;
1350 input array<Integer> ass2;
1351 output list<Integer> unsolvables = {};
1352 protected
1353 BackendDAE.AdjacencyMatrixElementEnhanced elem;
1354 Boolean isUnsolvable;
1355 algorithm
1356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4147 times.
751855 for index in 1:size loop
1357 /* unmatched var */
1358
2/2
✓ Branch 1 taken 421575 times.
✓ Branch 2 taken 326133 times.
747708 if intLt(ass1[index],0) then
1359 421575 elem := meT[index];
1360 /* consider only unmatched eqns */
1361 421575 elem := removeMatched(elem,ass2);
1362 421575 isUnsolvable := unsolvable(elem);
1363
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 421575 times.
421575 if isUnsolvable then
1364 unsolvables := index::unsolvables;
1365 end if;
1366 end if;
1367 end for;
1368 end getUnsolvableVarsConsiderMatching;
1369
1370
1371 protected function removeMatched
1372 " helper function for getUnsolvableVarsConsiderMatching,
1373 returns only unmatched equations
1374 author: Frenkel TUD 2012-08"
1375 input BackendDAE.AdjacencyMatrixElementEnhanced elem;
1376 input array<Integer> ass2;
1377 output BackendDAE.AdjacencyMatrixElementEnhanced oAcc = {};
1378 protected
1379 Integer e;
1380 algorithm
1381
2/2
✓ Branch 0 taken 1490344 times.
✓ Branch 1 taken 421575 times.
1911919 for el in elem loop
1382 1490344 (e,_,_) := el;
1383
3/4
✓ Branch 0 taken 1489796 times.
✓ Branch 1 taken 548 times.
✓ Branch 3 taken 1489796 times.
✗ Branch 4 not taken.
1490344 if intGt(e,0) and intLt(ass2[e],0) then
1384 oAcc := el::oAcc;
1385 end if;
1386 end for;
1387 end removeMatched;
1388
1389
1390 protected function calcVarWeights "function giving points for solvability"
1391 input Integer v;
1392 input BackendDAE.AdjacencyMatrixTEnhanced mt;
1393 input array<Integer> ass2;
1394 input array<Integer> iPoints;
1395 output array<Integer> oPoints;
1396 protected
1397 Integer p;
1398 algorithm
1399 341 p := calcSolvabilityWeight(mt[v],ass2);
1400 341 oPoints := arrayUpdate(iPoints,v,p);
1401 end calcVarWeights;
1402
1403
1404 protected function calcSolvabilityWeight
1405 "helper function for calcVarWeights, giving points for solvability"
1406 input BackendDAE.AdjacencyMatrixElementEnhanced inRow;
1407 input array<Integer> ass2;
1408 output Integer w;
1409 algorithm
1410 341 w := List.fold1(inRow,solvabilityWeightsnoStates,ass2,0);
1411 end calcSolvabilityWeight;
1412
1413
1414 protected function solvabilityWeightsnoStates
1415 "helper function for calcSolvabilityWeight, giving points for solvability
1416 author: Frenkel TUD 2012-05"
1417 input tuple<Integer,BackendDAE.Solvability,BackendDAE.Constraints> inTpl;
1418 input array<Integer> ass;
1419 input Integer iW;
1420 output Integer oW;
1421 algorithm
1422 oW := match inTpl
1423 local
1424 BackendDAE.Solvability s;
1425 Integer eq,w;
1426 case (eq,s,_)
1427 guard
1428 intGt(eq,0) and
1429 not intGt(ass[eq], 0)
1430 algorithm
1431 912 w := solvabilityWeights(s);
1432 912 then
1433 intAdd(w,iW);
1434 else iW;
1435 end match;
1436 end solvabilityWeightsnoStates;
1437
1438
1439 protected function solvabilityWeights
1440 " helper function for solvabilityWeightsnoStates
1441 author: Frenkel TUD 2012-05,
1442 return a integer for the solvability, this function is used
1443 to calculade weights for variables to select the tearing variable."
1444 input BackendDAE.Solvability solva;
1445 output Integer i;
1446 algorithm
1447 i := match solva
1448 case BackendDAE.SOLVABILITY_SOLVED() then 0;
1449 case BackendDAE.SOLVABILITY_CONSTONE() then 2;
1450 case BackendDAE.SOLVABILITY_CONST() then 5;
1451 case BackendDAE.SOLVABILITY_PARAMETER(b=false) then 0;
1452 case BackendDAE.SOLVABILITY_PARAMETER(b=true) then 50;
1453 case BackendDAE.SOLVABILITY_LINEAR(b=false) then 0;
1454 case BackendDAE.SOLVABILITY_LINEAR(b=true) then 100;
1455 case BackendDAE.SOLVABILITY_NONLINEAR() then 200;
1456 case BackendDAE.SOLVABILITY_UNSOLVABLE() then 300;
1457 else 0;
1458 end match;
1459 end solvabilityWeights;
1460
1461
1462 protected function addEqnWeights
1463 "function adds five points to variables for each equation it would causalize as tvar"
1464 input Integer e;
1465 input BackendDAE.AdjacencyMatrixEnhanced m;
1466 input array<Integer> ass1;
1467 input array<Integer> iPoints;
1468 output array<Integer> oPoints;
1469 algorithm
1470 oPoints := matchcontinue iPoints
1471 local
1472 Integer v1,v2;
1473 array<Integer> points;
1474 case _
1475 algorithm
1476 // finds equations with exact two variables (v1,v2)
1477
6/6
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 369 times.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 357 times.
✓ Branch 6 taken 132 times.
✓ Branch 7 taken 225 times.
373 (v1,_,_)::(v2,_,_)::{} := List.removeOnTrue(ass1, isAssignedSaveEnhanced, m[e]);
1478 225 points := arrayUpdate(iPoints,v1,iPoints[v1]+5);
1479 225 points := arrayUpdate(iPoints,v2,points[v2]+5);
1480 then
1481 points;
1482 else
1483 iPoints;
1484 end matchcontinue;
1485 end addEqnWeights;
1486
1487
1488 protected function isAssignedSaveEnhanced " returns true if var/eqn is already assigned
1489 author: Frenkel TUD 2012-05"
1490 input array<Integer> ass;
1491 input tuple<Integer,BackendDAE.Solvability,BackendDAE.Constraints> inTpl;
1492 output Boolean outB;
1493 algorithm
1494 outB := match inTpl
1495 local
1496 Integer i;
1497 case (i,_,_) guard intGt(i,0)
1498 1533553 then
1499 intGt(ass[i],0);
1500 else
1501 true;
1502 end match;
1503 end isAssignedSaveEnhanced;
1504
1505
1506 protected function discriminateDiscrete " leaves only one-tenth of points for each discrete variable
1507 author: Frenkel TUD 2012-08"
1508 input Integer v;
1509 input BackendDAE.Variables vars;
1510 input array<Integer> iPoints;
1511 output array<Integer> oPoints;
1512 protected
1513 Integer p;
1514 Boolean b;
1515 BackendDAE.Var var;
1516 algorithm
1517 341 var := BackendVariable.getVarAt(vars, v);
1518 341 b := BackendVariable.isVarDiscrete(var);
1519 341 p := iPoints[v];
1520
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 337 times.
341 p := if b then intDiv(p,10) else p;
1521 341 oPoints := arrayUpdate(iPoints,v,p);
1522 end discriminateDiscrete;
1523
1524
1525 protected function selectVarWithMostPoints " returns one var with most points
1526 author: Frenkel TUD 2012-05"
1527 input list<Integer> vars;
1528 input list<Integer> points;
1529 output Integer oVar = -1;
1530 protected
1531 Integer defp = -1;
1532 Integer p;
1533 algorithm
1534
2/2
✓ Branch 0 taken 341 times.
✓ Branch 1 taken 44 times.
385 for v in vars loop
1535 341 p := listGet(points,v);
1536
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 241 times.
341 if p > defp then
1537 defp := p;
1538 oVar := v;
1539 end if;
1540 end for;
1541 end selectVarWithMostPoints;
1542
1543
1544 protected function tearingBFS " function to find maximum matching
1545 author: Frenkel TUD 2012-05"
1546 input BackendDAE.AdjacencyMatrixElementEnhanced queue;
1547 input BackendDAE.AdjacencyMatrixEnhanced m;
1548 input BackendDAE.AdjacencyMatrixTEnhanced mt;
1549 input array<list<Integer>> mapEqnIncRow;
1550 input array<Integer> mapIncRowEqn;
1551 input Integer size;
1552 input array<Integer> ass1;
1553 input array<Integer> ass2;
1554 //input array<Integer> columark;
1555 //input Integer mark;
1556 input BackendDAE.AdjacencyMatrixElementEnhanced nextQueue;
1557 algorithm
1558 () := match(queue, nextQueue)
1559 local
1560 Integer c,eqnsize,cnonscalar;
1561 BackendDAE.AdjacencyMatrixElementEnhanced rest,newqueue,rows;
1562 // if there are no more equations in queue maximum matching is found
1563 case ({}, {}) then ();
1564
1565 // if queue is empty, use next queue
1566 case ({}, _)
1567 algorithm
1568 // use only equations from next queue which are not assigned yet
1569 145 newqueue := List.removeOnTrue(ass2, isAssignedSaveEnhanced, nextQueue);
1570 // use linear equations first
1571 145 newqueue := sortEqnsSolvable(newqueue,m);
1572
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 145 times.
145 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1573 ✗ print("Use next Queue!\n");
1574 end if;
1575 145 tearingBFS(newqueue,m,mt,mapEqnIncRow,mapIncRowEqn,size,ass1,ass2,{});
1576 then
1577 ();
1578 case((c,_,_)::rest, _)
1579 algorithm
1580
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 470 times.
470 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1581 ✗ print("Queue:\n");
1582 ✗ BackendDump.dumpAdjacencyRowEnhanced(queue);
1583 ✗ print("Process Eqn: " + intString(c) + "\n");
1584 end if;
1585 // not assigned variables in equation c:
1586 470 rows := List.removeOnTrue(ass1, isAssignedSaveEnhanced, m[c]);
1587 //arrayUpdate(columark,c,mark);
1588 // For Equationarrays
1589 470 cnonscalar := mapIncRowEqn[c];
1590 470 eqnsize := listLength(mapEqnIncRow[cnonscalar]);
1591
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 470 times.
470 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1592 ✗ print("Eqn Size: " + intString(eqnsize) + "\n");
1593 // fcall(Flags.TEARING_DUMPVERBOSE, print,"Rows(not assigned variables in eqn " + intString(c) + ":\n" + stringDelimitList(List.mapMap(rows,Util.tuple21,intString),", ") + "\n");
1594 ✗ print("Rows (not assigned variables in eqn " + intString(c) + "):\n");
1595 ✗ BackendDump.dumpAdjacencyRowEnhanced(rows);
1596 ✗ print("\n");
1597 end if;
1598 // make assignment and find next equations to get causalized
1599 470 newqueue := tearingBFS1(rows,eqnsize,mapEqnIncRow[cnonscalar],mt,ass1,ass2,nextQueue);
1600
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 470 times.
470 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1601 ✗ print("Next Queue:\n");
1602 ✗ BackendDump.dumpAdjacencyRowEnhanced(newqueue);
1603 ✗ print("\n\n");
1604 end if;
1605 470 tearingBFS(rest,m,mt,mapEqnIncRow,mapIncRowEqn,size,ass1,ass2,newqueue);
1606 then
1607 ();
1608 end match;
1609 end tearingBFS;
1610
1611
1612 protected function sortEqnsSolvable
1613 " author: Frenkel TUD 2012-10
1614 moves equations with nonlinear or unsolvable parts to the end"
1615 input BackendDAE.AdjacencyMatrixElementEnhanced queue;
1616 input BackendDAE.AdjacencyMatrixEnhanced m;
1617 output BackendDAE.AdjacencyMatrixElementEnhanced nextQueue;
1618 protected
1619 BackendDAE.AdjacencyMatrixElementEnhanced qnon,qsolv;
1620 algorithm
1621 145 (qnon,qsolv) := List.split1OnTrue(queue,hasnonlinearVars,m);
1622 145 nextQueue := listAppend(qsolv,qnon);
1623 end sortEqnsSolvable;
1624
1625
1626 protected function hasnonlinearVars
1627 input BackendDAE.AdjacencyMatrixElementEnhancedEntry entry;
1628 input BackendDAE.AdjacencyMatrixEnhanced m;
1629 output Boolean hasnonlinear;
1630 protected
1631 Integer r;
1632 BackendDAE.AdjacencyMatrixElementEnhanced row;
1633 algorithm
1634 263 (r,_,_) := entry;
1635 263 row := m[r];
1636 263 hasnonlinear := hasnonlinearVars1(row);
1637 end hasnonlinearVars;
1638
1639
1640 protected function hasnonlinearVars1
1641 input BackendDAE.AdjacencyMatrixElementEnhanced row;
1642 output Boolean hasnonlinear;
1643 algorithm
1644 hasnonlinear := match row
1645 local
1646 BackendDAE.AdjacencyMatrixElementEnhanced rest;
1647 case {} then false;
1648 case (_,BackendDAE.SOLVABILITY_NONLINEAR(),_)::_
1649 then
1650 true;
1651 case _::rest
1652 731 then
1653 hasnonlinearVars1(rest);
1654 end match;
1655 end hasnonlinearVars1;
1656
1657
1658 protected function tearingBFS1 " function checks for possible assignments and calls tearingBFS2
1659 author: Frenkel TUD 2012-05"
1660 input BackendDAE.AdjacencyMatrixElementEnhanced rows;
1661 input Integer size;
1662 input list<Integer> c;
1663 input BackendDAE.AdjacencyMatrixTEnhanced mt;
1664 input array<Integer> ass1;
1665 input array<Integer> ass2;
1666 //input array<Integer> columark;
1667 //input Integer mark;
1668 input BackendDAE.AdjacencyMatrixElementEnhanced inNextQueue;
1669 output BackendDAE.AdjacencyMatrixElementEnhanced outNextQueue;
1670 algorithm
1671 outNextQueue := match inNextQueue
1672 local
1673 // there is only one variable assignable from this equation and the equation is solvable for this variable
1674 case _
1675 guard
1676 intEq(listLength(rows),size) and
1677 solvableLst(rows)
1678 algorithm
1679
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 203 times.
203 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1680 ✗ print("Assign Eqns: " + stringDelimitList(List.map(c,intString),", ") + "\n");
1681 end if;
1682 203 then
1683 // make assignment and get next equations
1684 tearingBFS2(rows,c,mt,ass1,ass2,inNextQueue);
1685 /* case (_,_,_,_,_,_,_)
1686 guard
1687 intEq(listLength(rows),size) and
1688 not solvableLst(rows);
1689 algorithm
1690 //fcall(Flags.TEARING_DUMPVERBOSE, print,"cannot Assign Var" + intString(r) + " with Eqn " + intString(c) + "\n");
1691 then
1692 inNextQueue;
1693 */
1694 else inNextQueue;
1695 end match;
1696 end tearingBFS1;
1697
1698
1699 protected function solvableLst
1700 " returns true if all variables are solvable"
1701 input BackendDAE.AdjacencyMatrixElementEnhanced rows;
1702 output Boolean solvable = true;
1703 protected
1704 BackendDAE.Solvability s;
1705 algorithm
1706
2/2
✓ Branch 0 taken 511965 times.
✓ Branch 1 taken 212600 times.
724565 for r in rows loop
1707 511965 (_,s,_) := r;
1708
2/2
✓ Branch 1 taken 59932 times.
✓ Branch 2 taken 452033 times.
511965 if not solvable(s) then
1709 solvable := false;
1710 59932 return;
1711 end if;
1712 end for;
1713 end solvableLst;
1714
1715
1716 protected function solvable
1717 input BackendDAE.Solvability s;
1718 output Boolean b;
1719 algorithm
1720 b := match s
1721 case BackendDAE.SOLVABILITY_SOLVED() then true;
1722 case BackendDAE.SOLVABILITY_CONSTONE() then true;
1723 case BackendDAE.SOLVABILITY_CONST(b=b) then b;
1724
5/6
✓ Branch 0 taken 129933 times.
✓ Branch 1 taken 17949 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 129925 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
147882 case BackendDAE.SOLVABILITY_PARAMETER(b=b) then (b and not stringEqual(Flags.getConfigString(Flags.TEARING_STRICTNESS), "veryStrict"));
1725 case BackendDAE.SOLVABILITY_LINEAR() then false;
1726 case BackendDAE.SOLVABILITY_NONLINEAR() then false;
1727 case BackendDAE.SOLVABILITY_UNSOLVABLE() then false;
1728 case BackendDAE.SOLVABILITY_SOLVABLE() then true;
1729 else false;
1730 end match;
1731 end solvable;
1732
1733 protected function isEntrySolved
1734 input BackendDAE.AdjacencyMatrixElementEnhancedEntry entry;
1735 output Boolean b;
1736 algorithm
1737 b := match entry
1738 case (_, BackendDAE.SOLVABILITY_SOLVED(), _) then true;
1739 case (_, BackendDAE.SOLVABILITY_PARAMETER(b=b), _) algorithm
1740 ✗ Error.addInternalError("SOLVABILITY_PARAMETER is not handled yet. Requires revision.", sourceInfo());
1741 ✗ then (b and not stringEqual(Flags.getConfigString(Flags.TEARING_STRICTNESS), "veryStrict"));
1742 else false;
1743 end match;
1744 end isEntrySolved;
1745
1746 protected function isEntrySolvable
1747 input BackendDAE.AdjacencyMatrixElementEnhancedEntry entry;
1748 output Boolean b;
1749 algorithm
1750 10 b := solvable(Util.tuple32(entry));
1751 end isEntrySolvable;
1752
1753
1754 protected function tearingBFS2 " function to make an assignment and determine the next equations for queue
1755 author: Frenkel TUD 2012-05"
1756 input BackendDAE.AdjacencyMatrixElementEnhanced rows;
1757 input list<Integer> clst;
1758 input BackendDAE.AdjacencyMatrixTEnhanced mt;
1759 input array<Integer> ass1;
1760 input array<Integer> ass2;
1761 //input array<Integer> columark;
1762 //input Integer mark;
1763 input BackendDAE.AdjacencyMatrixElementEnhanced inNextQueue;
1764 output BackendDAE.AdjacencyMatrixElementEnhanced outNextQueue;
1765 algorithm
1766 outNextQueue := match(rows, clst)
1767 local
1768 Integer r,c;
1769 list<Integer> ilst;
1770 BackendDAE.AdjacencyMatrixElementEnhanced rest,vareqns,newqueue;
1771 case ({}, _) then inNextQueue;
1772 case ((r,_,_)::rest, c::ilst)
1773 algorithm
1774
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 207 times.
207 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1775 ✗ print("Assignment: Eq " + intString(c) + " - Var " + intString(r) + "\n");
1776 end if;
1777 // assign
1778 207 arrayUpdate(ass1,r,c);
1779 207 arrayUpdate(ass2,c,r);
1780
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 207 times.
207 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1781 ✗ print("ass1: " + stringDelimitList(List.mapArray(ass1, intString),",")+"\n");
1782 ✗ print("ass2: " + stringDelimitList(List.mapArray(ass2, intString),",")+"\n");
1783 end if;
1784 // not yet assigned equations containing var r
1785 207 vareqns := List.removeOnTrue(ass2, isAssignedSaveEnhanced, mt[r]);
1786 207 newqueue := listAppend(inNextQueue,vareqns);
1787 207 then
1788 tearingBFS2(rest,ilst,mt,ass1,ass2,newqueue);
1789 end match;
1790 end tearingBFS2;
1791
1792
1793 protected function omcTearing3 " function to rerun omcTearing2 if there are still unassigned vars
1794 author: Frenkel TUD 2012-05"
1795 input list<Integer> unassigned;
1796 input list<Integer> unsolvables;
1797 input list<Integer> tSel_always;
1798 input list<Integer> tSel_prefer;
1799 input list<Integer> tSel_avoid;
1800 input list<Integer> tSel_never;
1801 input BackendDAE.AdjacencyMatrixEnhanced m;
1802 input BackendDAE.AdjacencyMatrixTEnhanced mt;
1803 input array<list<Integer>> mapEqnIncRow;
1804 input array<Integer> mapIncRowEqn;
1805 input Integer size;
1806 input BackendDAE.Variables vars;
1807 input BackendDAE.Shared ishared;
1808 input array<Integer> ass1;
1809 input array<Integer> ass2;
1810 input array<Integer> columark;
1811 input Integer mark;
1812 input list<Integer> inTVars;
1813 output list<Integer> outTVars;
1814 output Integer oMark;
1815 algorithm
1816 (outTVars,oMark) := match unassigned
1817 local
1818 35 case {} then (inTVars,mark);
1819 else
1820 algorithm
1821 39 (outTVars,oMark) := omcTearing2(unsolvables,tSel_always,tSel_prefer,tSel_avoid,tSel_never,m,mt,mapEqnIncRow,mapIncRowEqn,size,vars,ishared,ass1,ass2,columark,mark,inTVars);
1822 then
1823 (outTVars,oMark);
1824 end match;
1825 end omcTearing3;
1826
1827
1828 protected function omcTearing4
1829 " maps indexes back to entire system and creates strong component from tearing information
1830 author: Frenkel TUD 2012-09"
1831 input BackendDAE.JacobianType jacType;
1832 input BackendDAE.EqSystem isyst;
1833 input BackendDAE.Shared ishared;
1834 input BackendDAE.EqSystem subsyst;
1835 input list<Integer> tvars;
1836 input list<Integer> residual;
1837 input array<Integer> ass1;
1838 input array<Integer> ass2;
1839 input list<list<Integer>> othercomps;
1840 input list<Integer> eindex;
1841 input list<Integer> vindx;
1842 input array<list<Integer>> mapEqnIncRow;
1843 input array<Integer> mapIncRowEqn;
1844 input array<Integer> columark;
1845 input Integer mark;
1846 input Boolean mixedSystem;
1847 output BackendDAE.StrongComponent ocomp;
1848 output Boolean outRunMatching;
1849 algorithm
1850 (ocomp,outRunMatching):=
1851 matchcontinue mixedSystem
1852 local
1853 list<Integer> ores,residual1,ovars;
1854 BackendDAE.InnerEquations innerEquations;
1855 array<Integer> eindxarr,varindxarr;
1856 Boolean linear;
1857 case _
1858 algorithm
1859
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
1860 ✗ print("handle torn System\n");
1861 end if;
1862 30 residual1 := List.map1r(residual,arrayGet,mapIncRowEqn);
1863 30 residual1 := List.fold2(residual1,uniqueIntLst,mark,columark,{});
1864 // map indexes back
1865 30 eindxarr := listArray(eindex);
1866 30 ores := List.map1r(residual1,arrayGet,eindxarr);
1867 30 varindxarr := listArray(vindx);
1868 30 ovars := List.map1r(tvars,arrayGet,varindxarr);
1869 30 innerEquations := omcTearing4_1(othercomps,ass2,mapIncRowEqn,eindxarr,varindxarr,columark,mark);
1870 30 linear := BackendDAEUtil.getLinearfromJacType(jacType);
1871
4/4
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 22 times.
66 then
1872 (BackendDAE.TORNSYSTEM(BackendDAE.TEARINGSET(ovars, ores, innerEquations, BackendDAE.EMPTY_JACOBIAN()), NONE(), linear,mixedSystem),true);
1873 case _
1874 ✗ then
1875 (BackendDAE.TORNSYSTEM(BackendDAE.TEARINGSET({}, {}, {}, BackendDAE.EMPTY_JACOBIAN()), NONE(), false,mixedSystem),false);
1876 end matchcontinue;
1877 end omcTearing4;
1878
1879
1880 protected function omcTearing4_1
1881 " creates innerEquations for TearingSet
1882 author: Frenkel TUD 2012-09"
1883 input list<list<Integer>> othercomps;
1884 input array<Integer> ass2;
1885 input array<Integer> mapIncRowEqn;
1886 input array<Integer> eindxarr;
1887 input array<Integer> varindxarr;
1888 input array<Integer> columark;
1889 input Integer mark;
1890 output BackendDAE.InnerEquations outInnerEquations;
1891 algorithm
1892
4/4
✓ Branch 0 taken 203 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 203 times.
✓ Branch 3 taken 30 times.
233 outInnerEquations := list(
1893 match x
1894 local
1895 list<Integer> vlst,clst,elst;
1896 Integer e,v,c;
1897
1898 case {c}
1899 algorithm
1900 201 e := mapIncRowEqn[c];
1901 201 e := eindxarr[e];
1902 201 v := ass2[c];
1903 201 v := varindxarr[v];
1904 201 then
1905 BackendDAE.INNEREQUATION(eqn=e,vars={v});
1906
1907 case clst
1908 algorithm
1909 2 elst := List.map1r(clst,arrayGet,mapIncRowEqn);
1910 2 elst := List.fold2(elst,uniqueIntLst,mark,columark,{});
1911
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 {e} := elst;
1912 2 e := eindxarr[e];
1913 2 vlst := List.map1r(clst,arrayGet,ass2);
1914 2 vlst := List.map1r(vlst,arrayGet,varindxarr);
1915 2 then
1916 BackendDAE.INNEREQUATION(eqn=e,vars=vlst);
1917 end match
1918 for x in othercomps);
1919 end omcTearing4_1;
1920
1921
1922 // ============================================================================
1923 // Section for minimal tearing
1924 // Tear only the minimal amount of variables from strong components which are
1925 // all discrete variables and CSE variables.
1926 // ============================================================================
1927 protected function minimalTearing
1928 "Tears discrete variables from Loops.
1929 Returns torn system with discrete variables as tearing variables."
1930 input BackendDAE.EqSystem isyst;
1931 input BackendDAE.Shared ishared;
1932 input list<Integer> eindex;
1933 input list<Integer> vindx;
1934 input BackendDAE.JacobianType jacType;
1935 input Boolean mixedSystem;
1936 output BackendDAE.StrongComponent ocomp;
1937 protected
1938 Integer size, qidx, vidx;
1939 array<Integer> nE, nV;
1940 array<Boolean> varArray, eqArray;
1941 list<Integer> unsolvedDiscreteVars, unsolvedCSEVars, unsolvedCombined, algSolvedVars;
1942 list<Integer> iterationVars = {}, residualequations = {};
1943 list<BackendDAE.Var> var_lst;
1944 list<BackendDAE.Equation> eqn_lst;
1945 BackendDAE.InnerEquations innerEquationsLocalIndex = {}, innerEquations;
1946 BackendDAE.AdjacencyMatrixEnhanced adjEnh, adjEnhT;
1947 Boolean linear;
1948 BackendDAE.EquationArray eqns;
1949 BackendDAE.EqSystem subsyst;
1950 BackendDAE.Variables vars;
1951 algorithm
1952 34 linear := BackendDAEUtil.getLinearfromJacType(jacType);
1953
1954 try
1955
1956 // Create a local subsystem to simplify processing. This is not neccessary per-se but is helpful.
1957 // A little bit of cost is something we can live with for getting a clear view of the system.
1958 34 eqn_lst := BackendEquation.getList(eindex,BackendEquation.getEqnsFromEqSystem(isyst));
1959 34 eqns := BackendEquation.listEquation(eqn_lst);
1960 34 var_lst := List.map1r(vindx, BackendVariable.getVarAt, BackendVariable.daeVars(isyst));
1961 34 vars := BackendVariable.listVar1(var_lst);
1962 34 subsyst := BackendDAEUtil.createEqSystem(vars, eqns);
1963
1964 34 (adjEnh,adjEnhT) := BackendDAEUtil.getAdjacencyMatrixEnhanced(subsyst, ishared, BackendDAEUtil.isInitializationDAE(ishared));
1965
1966 // print("Minimal Tearing subsystem: \n");
1967 // BackendDump.printEqSystem(subsyst);
1968
1969 34 size := listLength(vindx);
1970 34 varArray := arrayCreate(size,true);
1971 34 eqArray := arrayCreate(size,true);
1972 34 nE := arrayCreate(size,-1);
1973 34 nV := arrayCreate(size,-1);
1974
1975 // find discrete vars. Warn on tearing select always and prefer on discrete vars.
1976 34 unsolvedDiscreteVars := findDiscreteWarnTearingSelect(var_lst);
1977 // print("All discrete Vars: " + stringDelimitList(List.map(unsolvedDiscreteVars,intString),",") + "\n");
1978
1979 // also find $cse vars and try to make them inner vars since they have implicit '__OpenModelica_tearingSelect = TearingSelect.never'
1980 34 unsolvedCSEVars := findCSE(var_lst);
1981 // print("All CSE Vars: " + stringDelimitList(List.map(unsolvedCSEVars,intString),",") + "\n");
1982
1983 34 unsolvedCombined := listReverse(List.uniqueIntN(listAppend(unsolvedDiscreteVars, unsolvedCSEVars),listLength(var_lst)));
1984 // print("All discrete+CSE Vars: " + stringDelimitList(List.map(unsolvedCombined,intString),",") + "\n");
1985
1986 // Look for algorithm equations. If there is an algorithm equation
1987 // remove all discrete variables solved in it. The algorithm is added as
1988 // inner equation.
1989 qidx := 1;
1990
2/2
✓ Branch 0 taken 2226 times.
✓ Branch 1 taken 34 times.
2260 for eqn in eqn_lst loop
1991
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2226 times.
2226 if BackendEquation.isAlgorithm(eqn) then
1992 // mark the alg eqn to be ignored for later
1993 // matching.
1994 ✗ eqArray[qidx] := false;
1995
1996 algSolvedVars := {};
1997 ✗ for entr in adjEnh[qidx] loop
1998 // var is solved in this algorithm.
1999 ✗ if isEntrySolved(entr) then
2000 ✗ (vidx,_,_) := entr;
2001 algSolvedVars := vidx::algSolvedVars;
2002 ✗ unsolvedCombined := List.deleteMemberOnTrue(vidx, unsolvedCombined, intEq);
2003
2004 // mark the var to be ignored for later
2005 // matching.
2006 ✗ varArray[vidx] := false;
2007 end if;
2008 end for;
2009
2010 // create an inner equation for the algorithm.
2011 ✗ innerEquationsLocalIndex := BackendDAE.INNEREQUATION(qidx, algSolvedVars)::innerEquationsLocalIndex;
2012 end if;
2013 2226 qidx := qidx + 1;
2014 end for;
2015 // print("Non-algorithm-output Vars: " + stringDelimitList(List.map(unsolvedCombined,intString),",") + "\n");
2016
2017 // Match the remaining discrete variables
2018
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 29 times.
34 if not listEmpty(unsolvedCombined) then
2019 5 matchDiscreteVars(unsolvedCombined, adjEnhT, varArray, eqArray, nE, nV);
2020 // make inner equations for the matched non-algorithm-output discrete vars.
2021 5 (varArray, eqArray, innerEquations) := getTearingSetfromAssign(unsolvedCombined, nE, varArray, eqArray);
2022
2023
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 5 times.
11 for iq in innerEquations loop
2024 innerEquationsLocalIndex := iq::innerEquationsLocalIndex;
2025 end for;
2026
2027 end if;
2028
2029 // Mark all other equations as residual and all other vars
2030 // as tearing vars.
2031 // This can be improved a bit to be clearer.
2032
1/2
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
2260 for i in 1:listLength(eindex) loop
2033
2/2
✓ Branch 1 taken 2220 times.
✓ Branch 2 taken 6 times.
2226 if eqArray[i] then
2034 residualequations := i::residualequations;
2035 end if;
2036
2037 // This ordering of iteration vars based on the ordering
2038 // in the normal adjacency matrix seems to cause differences
2039 // in sumulation time.
2040 // What is odd is that the order in adj matrix is not really guided
2041 // by anything. Somehow this seems to be faster for some models.
2042 // However, we now use the enahnced matrix which can sometimes have
2043 // the var entries for an equation slightly different order than the normal
2044 // adj matix.
2045 // Simulation should not be affected by what order we have here but it is.
2046 // There is something in later phases that operates according to this order
2047 // But it should not. This is just random order.
2048 /*
2049 for elem in aMatrix[i] loop
2050 if elem > 0 then
2051 if varArray[elem] then
2052 arrayUpdate(varArray,elem,false);
2053 iterationVars := elem::iterationVars;
2054 end if;
2055 end if;
2056 end for;
2057 */
2058
2059 // This performs a bit worse but still better than below.
2060 /*
2061 for entry in adjEnh[i] loop
2062 (vidx,_,_) := entry;
2063 if vidx > 0 then
2064 if varArray[vidx] then
2065 varArray[vidx] := false;
2066 iterationVars := vidx::iterationVars;
2067 end if;
2068 end if;
2069 end for;
2070 */
2071
2072 end for;
2073
2074 // Mark all other vars as iteration vars.
2075 // This should be all we need here. The ordering issue needs to be investigated later.
2076
1/2
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
2260 for i in 1:listLength(vindx) loop
2077
2/2
✓ Branch 1 taken 2220 times.
✓ Branch 2 taken 6 times.
2226 if varArray[i] then
2078 iterationVars := i::iterationVars;
2079 end if;
2080 end for;
2081
2082 // dumpTearingSetGlobalIndexes(BackendDAE.TEARINGSET(iterationVars, residualequations, listReverse(innerEquationsLocalIndex), BackendDAE.EMPTY_JACOBIAN()),size," - STRICT SET");
2083
2084 // Start converting local indeces of variables and equations to their global
2085 // counterparts (We used a smaller local subsystem for processing the system above.)
2086
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 34 times.
40 innerEquations := list(
2087 match ieqn
2088 case BackendDAE.INNEREQUATION() algorithm
2089 6 ieqn.vars := selectFromList_rev(vindx, ieqn.vars);
2090 6 ieqn.eqn := listGet(eindex,ieqn.eqn);
2091 then ieqn;
2092 else fail();
2093 end match for ieqn in innerEquationsLocalIndex);
2094
2095 34 iterationVars := selectFromList_rev(vindx, iterationVars);
2096 34 residualequations := selectFromList_rev(eindex, residualequations);
2097
2098 // dumpTearingSetGlobalIndexes(BackendDAE.TEARINGSET(iterationVars, residualequations, listReverse(innerEquations), BackendDAE.EMPTY_JACOBIAN()),size," - STRICT SET");
2099
2100 // Return torn system
2101
3/4
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 20 times.
✓ Branch 7 taken 14 times.
88 ocomp := BackendDAE.TORNSYSTEM(BackendDAE.TEARINGSET(listReverse(iterationVars), listReverse(residualequations), listReverse(innerEquations), BackendDAE.EMPTY_JACOBIAN()), NONE(), linear, mixedSystem);
2102 else
2103 ✗ Error.addInternalError("function minimalTearing failed", sourceInfo());
2104 ✗ fail();
2105 end try;
2106 end minimalTearing;
2107
2108
2109 protected function matchDiscreteVars
2110 "Matches all discrete vars given by inDiscreteVars."
2111 input list<Integer> inDiscreteVars;
2112 input BackendDAE.AdjacencyMatrixEnhanced adjEnhT;
2113 input array<Boolean> varArray;
2114 input array<Boolean> eqArray;
2115 input output array<Integer> nE "Equations";
2116 input output array<Integer> nV "Variables";
2117 protected
2118 array<Boolean> eqMarker;
2119 algorithm
2120 try
2121
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
11 for varIdx in inDiscreteVars loop
2122 6 eqMarker := arrayCopy(eqArray);
2123
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 (eqMarker, nE, nV, true) := pathFound(varIdx, adjEnhT, varArray, eqArray, eqMarker, nE, nV);
2124 end for;
2125 else
2126 ✗ Error.addInternalError("function matchDiscreteVars failed", sourceInfo());
2127 ✗ fail();
2128 end try;
2129 end matchDiscreteVars;
2130
2131 protected function pathFound
2132 "Tries to find a path in the bipartit graph with respect to solvability for
2133 matching of discrete variables."
2134 input Integer varIdx;
2135 input BackendDAE.AdjacencyMatrixEnhanced adjEnhT;
2136 input array<Boolean> varArray;
2137 input array<Boolean> eqArray;
2138 input output array<Boolean> eqMarker;
2139 input output array<Integer> nE "Equations";
2140 input output array<Integer> nV "Variables";
2141 output Boolean success = false;
2142 protected
2143 Integer eqIdx;
2144 algorithm
2145 try
2146 // Try to find a path in given equation from adjacency matrix
2147
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 for entry in adjEnhT[varIdx] loop
2148 10 (eqIdx,_,_) := entry;
2149
3/4
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
10 if isEntrySolvable(entry) and eqIdx > 0 then
2150
2151
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 if eqArray[eqIdx] and nV[eqIdx] == -1 then
2152 // Path found
2153 6 nV[eqIdx] := varIdx;
2154 6 nE[varIdx] := eqIdx;
2155 6 success := true;
2156 6 return;
2157 end if;
2158
2159 end if;
2160 end for;
2161
2162 // If no path was found mark equation as false and call pathFound
2163 ✗ for entry in adjEnhT[varIdx] loop
2164 ✗ (eqIdx,_,_) := entry;
2165
2166 ✗ if isEntrySolvable(entry) and eqIdx > 0 then
2167 ✗ if eqMarker[eqIdx] then
2168 ✗ eqMarker[eqIdx] := false;
2169 ✗ (eqMarker, nE, nV , success) := pathFound(nV[eqIdx], adjEnhT, varArray, eqArray, eqMarker, nE, nV);
2170 end if;
2171 end if;
2172 ✗ if success then
2173 ✗ nV[eqIdx] := varIdx;
2174 ✗ nE[varIdx] := eqIdx;
2175 ✗ return;
2176 end if;
2177 end for;
2178
2179 else
2180 ✗ Error.addInternalError("function pathFound failed", sourceInfo());
2181 ✗ fail();
2182 end try;
2183 end pathFound;
2184
2185
2186 protected function getTearingSetfromAssign
2187 "Set equations matched with the discrete vars as inner equations"
2188 input list<Integer> inDiscreteVars;
2189 input array<Integer> assign1;
2190 input output array<Boolean> varArray;
2191 input output array<Boolean> equationArray;
2192 output BackendDAE.InnerEquations innerEquations = {};
2193 protected
2194 Integer eqIdx;
2195 algorithm
2196 try
2197 // Add matched discrete equation to inner equations
2198
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
11 for varIdx in inDiscreteVars loop
2199 6 arrayUpdate(varArray,varIdx,false);
2200 6 eqIdx := assign1[varIdx];
2201 6 arrayUpdate(equationArray,eqIdx,false);
2202 6 innerEquations := BackendDAE.INNEREQUATION(eqIdx, {varIdx})::innerEquations;
2203 end for;
2204
2205 else
2206 ✗ Error.addInternalError("function getTearingSetfromAssign failed", sourceInfo());
2207 ✗ fail();
2208 end try;
2209 end getTearingSetfromAssign;
2210
2211
2212 // =============================================================================
2213 //
2214 // Tearing from Book of Cellier
2215 //
2216 // =============================================================================
2217
2218 protected function CellierTearing " tearing method based on the method from book of Cellier
2219 author: ptaeuber FHB 2013-2016"
2220 input BackendDAE.EqSystem isyst;
2221 input BackendDAE.Shared ishared;
2222 input list<Integer> eindex;
2223 input list<Integer> vindx;
2224 input list<Integer> tearingSelect_always;
2225 input Option<list<tuple<Integer, Integer, BackendDAE.Equation>>> ojac;
2226 input BackendDAE.JacobianType jacType;
2227 input Boolean mixedSystem;
2228 input Integer strongComponentIndex;
2229 output BackendDAE.StrongComponent ocomp;
2230 output Boolean outRunMatching;
2231 protected
2232 Integer size, tornsize;
2233 array<Integer> ass1, ass2, mapIncRowEqn, eqnNonlinPoints;
2234 array<list<Integer>> mapEqnIncRow;
2235 list<Integer> OutTVars, residual, residual_coll, order, unsolvables, discreteVars, tSel_always, tSel_alwaysByUser, tSel_prefer, tSel_avoid, tSel_never;
2236 BackendDAE.InnerEquations innerEquations;
2237 BackendDAE.EqSystem subsyst;
2238 BackendDAE.Variables vars;
2239 BackendDAE.EquationArray eqns;
2240 BackendDAE.AdjacencyMatrix m;
2241 BackendDAE.AdjacencyMatrix mt;
2242 BackendDAE.AdjacencyMatrixEnhanced me;
2243 BackendDAE.AdjacencyMatrixTEnhanced meT;
2244 BackendDAE.BackendDAEType DAEtype;
2245 String DAEtypeStr;
2246 BackendDAE.TearingSet strictTearingSet;
2247 BackendDAE.StateSets stateSets;
2248 Option<BackendDAE.TearingSet> casualTearingSet;
2249 list<BackendDAE.Equation> eqn_lst;
2250 list<BackendDAE.Var> var_lst;
2251 Boolean linear,b,noDynamicStateSelection,dynamicTearing,dtTarget,forceDegree1,hasStartCycle;
2252 DAE.ComponentRef cref;
2253 list<DAE.ComponentRef> startBaseCrefs, valueCrefs, cyclicBaseCrefs;
2254 BackendDAE.AdjacencyMatrixTEnhanced meTFull;
2255 String s,modelName;
2256 constant Boolean debug = false;
2257 algorithm
2258 1956 linear := BackendDAEUtil.getLinearfromJacType(jacType);
2259 1956 BackendDAE.EQSYSTEM(stateSets = stateSets) := isyst;
2260 1956 noDynamicStateSelection := listEmpty(stateSets);
2261 1956 BackendDAE.SHARED(backendDAEType=DAEtype, info=BackendDAE.EXTRA_INFO(fileNamePrefix=modelName)) := ishared;
2262 1956 DAEtypeStr := BackendDump.printBackendDAEType2String(DAEtype);
2263
2264 // ticket #15433: the variable-driven degree-1 forced assignment (getNextDegree1Var) is only
2265 // needed - and only safe - for the initialization system, where a $START.x variable is
2266 // structurally linked to the state's equations but solvable in exactly one of them. Enabling it
2267 // for the simulation system reorders the Tarjan causalization for components that already
2268 // causalize correctly, changing tearing results. Restrict it to the initialization DAE; the
2269 // forcing is then further restricted, below, to the components that actually exhibit the defect
2270 // (a $START.x->x cycle).
2271 1956 forceDegree1 := BackendDAEUtil.isInitializationDAE(ishared);
2272
2273 // check if dynamic tearing is enabled for linear/nonlinear system.
2274 // Only the targets whose runtime can switch tearing sets ("wasm" is the same
2275 // code generator as "wasm-jit", emitting a standalone module).
2276 1956 dtTarget := listMember(Config.simCodeTarget(), {"C", "wasm-jit", "wasm"});
2277 dynamicTearing := match (Config.dynamicTearing(),linear,noDynamicStateSelection,DAEtypeStr,Flags.getConfigBool(Flags.DYNAMIC_TEARING_FOR_INITIALIZATION),dtTarget)
2278 case ("true",_,true,"simulation",_,true) then true;
2279 case ("true",_,true,"initialization",true,true) then true;
2280 case ("linear",true,true,"simulation",_,true) then true;
2281 case ("linear",true,true,"initialization",true,true) then true;
2282 case ("nonlinear",false,true,"simulation",_,true) then true;
2283 case ("nonlinear",false,true,"initialization",true,true) then true;
2284 else false;
2285 end match;
2286
2287
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1956 times.
1956 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2288 ✗ print("\n" + BORDER + "\nBEGINNING of CellierTearing\n\n");
2289 end if;
2290
2291 // Generate Subsystem to get the adjacency matrix
2292 1956 size := listLength(vindx);
2293 1956 eqn_lst := BackendEquation.getList(eindex,BackendEquation.getEqnsFromEqSystem(isyst));
2294 1956 eqns := BackendEquation.listEquation(eqn_lst);
2295 1956 var_lst := List.map1r(vindx, BackendVariable.getVarAt, BackendVariable.daeVars(isyst));
2296 1956 vars := BackendVariable.listVar1(var_lst);
2297 1956 subsyst := BackendDAEUtil.createEqSystem(vars, eqns);
2298 1956 (subsyst,m,mt,_,_) := BackendDAEUtil.getAdjacencyMatrixScalar(subsyst, BackendDAE.NORMAL(),NONE(), BackendDAEUtil.isInitializationDAE(ishared));
2299 if debug then execStat("Tearing.CellierTearing -> 1"); end if;
2300
2301 // Delete negative entries from adjacency matrix
2302 1956 m := Array.map(m,deleteNegativeEntries);
2303 1956 mt := Array.map(mt,deleteNegativeEntries);
2304
2305
3/4
✓ Branch 1 taken 1954 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1954 times.
1956 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2306 2 print("\n\n###BEGIN print Strong Component#####################\n(Function:CellierTearing)\n");
2307 2 BackendDump.printEqSystem(subsyst);
2308 2 print("\n###END print Strong Component#######################\n(Function:CellierTearing)\n\n\n");
2309 end if;
2310
2311
2312 // Determine strict tearing set
2313 // ******************************************
2314
2315
3/4
✓ Branch 1 taken 1954 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1954 times.
1956 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2316 2 print("\n\nDetermine STRICT TEARING SET\n" + BORDER + BORDER + "\n\n");
2317 end if;
2318
2319 // Get advanced adjacency matrix (determine how the variables occur in the equations)
2320 1956 (me,meT,mapEqnIncRow,mapIncRowEqn) := BackendDAEUtil.getAdjacencyMatrixEnhancedScalar(subsyst,ishared,false);
2321 if debug then execStat("Tearing.CellierTearing -> 1.5"); end if;
2322
2323 // Determine unsolvable vars to consider solvability
2324 1956 unsolvables := getUnsolvableVars(size,meT);
2325
2326 // ticket #15433: detect the degenerate "$START.x depends on x" cycle inside this strong
2327 // component and only then keep the degree-1 forced assignment enabled. The component is cyclic
2328 // when it contains BOTH a $START.x variable and its base value variable x: since a strong
2329 // component is an algebraic loop, the two are mutually reachable, i.e. x's start attribute
2330 // transitively depends on x itself (e.g. a fixed=false state whose start binds to a parameter
2331 // computed from the state - the cable models in #15433). Exactly this cycle produces the matching
2332 // defect that orphans $START.x and yields a structurally singular torn init system; the degree-1
2333 // forcing repairs it. For every OTHER component (no such cycle) the forcing is switched OFF so the
2334 // plain Cellier result - which is already correct there - is preserved (this is what keeps models
2335 // like Tearing16/Algorithm2 unchanged). A modeler warning is emitted, naming the self-dependent
2336 // variable (with subscripts), because a start value that depends on its own variable is an
2337 // ill-posed initialization; the transformational debugger can be used to inspect the full chain.
2338 hasStartCycle := false;
2339 cyclicBaseCrefs := {};
2340
2/2
✓ Branch 0 taken 930 times.
✓ Branch 1 taken 1026 times.
1956 if forceDegree1 then
2341 startBaseCrefs := {};
2342 valueCrefs := {};
2343
1/2
✓ Branch 0 taken 930 times.
✗ Branch 1 not taken.
24958 for i in 1:size loop
2344 24028 cref := BackendVariable.varCref(BackendVariable.getVarAt(vars, i));
2345
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24028 times.
24028 if ComponentReference.isStartCref(cref) then
2346 ✗ startBaseCrefs := ComponentReference.popCref(cref) :: startBaseCrefs;
2347 else
2348 valueCrefs := cref :: valueCrefs;
2349 end if;
2350 end for;
2351 // a $START.x whose base value variable x is also in this component closes the cycle
2352
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 930 times.
930 for baseCref in startBaseCrefs loop
2353 ✗ if List.isMemberOnTrue(baseCref, valueCrefs, ComponentReferenceBasics.crefEqual) then
2354 hasStartCycle := true;
2355 cyclicBaseCrefs := baseCref :: cyclicBaseCrefs;
2356 end if;
2357 end for;
2358 end if;
2359 // ticket #15433: a $START.x->x cycle is always an ill-posed start value (MLS 4.9.6 - the start
2360 // attribute of x cannot be computed from x). Always warn, regardless of whether the degree-1
2361 // forcing below is needed to keep initialization solvable, and let the modeler decide whether the
2362 // dependency is intended: even when initialization still succeeds, the start value (a guess) is
2363 // never actually used to initialize the variable.
2364
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1956 times.
1956 for baseCref in cyclicBaseCrefs loop
2365 ✗ Error.addCompilerWarning("Initialization start-value cycle: the start attribute of '" +
2366 ComponentReferenceBasics.printComponentRefStr(baseCref) +
2367 "' depends, directly or transitively, on the variable itself. The start attribute defines the " +
2368 "initial value of '" + ComponentReferenceBasics.printComponentRefStr(baseCref) + "' and therefore " +
2369 "cannot be computed from it, so this start value cannot be used to initialize the variable (see " +
2370 "Modelica Specification section 4.9.6). This is possibly a modeling error; if unintended, give '" +
2371 ComponentReferenceBasics.printComponentRefStr(baseCref) + "' a start value that does not depend on " +
2372 "itself. Use the transformational debugger to inspect the dependency chain.");
2373 end for;
2374 // ticket #15433: a $START.x->x cycle is NECESSARY but NOT SUFFICIENT to need the degree-1
2375 // forcing. Such a cycle is always an ill-posed start value (the start attribute of x cannot be
2376 // computed from x), but many initialization components with one (e.g. the lag/limiter blocks in
2377 // the OpenIPSL PSSE controllers) still tear and initialize correctly, because $START.x keeps its
2378 // own defining equation; forcing them only reorders a healthy system and breaks it. So we do NOT
2379 // decide here. We first run the plain Cellier pass below and only re-run it with forcing if that
2380 // plain pass actually orphans a $START.x variable, leaving a structurally singular torn set - the
2381 // cable defect. 'forceDegree1' stays the init-DAE eligibility flag, passed as 'false' to pass one.
2382
2383 // Determine a weight for the nonlinearity of each equation
2384 1956 eqnNonlinPoints := arrayCreate(size, -1);
2385 1956 getEquationNonlinearityPoints(eqnNonlinPoints, me, size);
2386 if debug then execStat("Tearing.CellierTearing -> 2"); end if;
2387
2388
3/4
✓ Branch 1 taken 1954 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1954 times.
1956 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2389 2 print("\nAdjacencyMatrixEnhanced:\n");
2390 2 BackendDump.dumpAdjacencyMatrixEnhanced(me);
2391 2 print("\nAdjacencyMatrixTransposedEnhanced:\n");
2392 2 BackendDump.dumpAdjacencyMatrixTEnhanced(meT);
2393 2 print("\neqLinPoints:\n" + stringDelimitList(List.mapArray(eqnNonlinPoints, intString),",") + "\n\n");
2394 end if;
2395
2396
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1956 times.
1956 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2397 ✗ print("mapEqnIncRow:"); //+ stringDelimitList(List.map(List.flatten(arrayList(mapEqnIncRow)),intString),",") + "\n\n");
2398 ✗ BackendDump.dumpAdjacencyMatrix(mapEqnIncRow);
2399 ✗ print("\nmapIncRowEqn:\n" + stringDelimitList(List.mapArray(mapIncRowEqn, intString),",") + "\n\n");
2400 ✗ print("\n\nUNSOLVABLES:\n" + stringDelimitList(List.map(unsolvables,intString),",") + "\n\n");
2401 end if;
2402
2403 // Determine discrete vars
2404 1956 discreteVars := findDiscrete(var_lst);
2405
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1956 times.
1956 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2406 ✗ print("\nDiscrete Vars:\n" + stringDelimitList(List.map(discreteVars,intString),",") + "\n\n");
2407 end if;
2408
2409 // Collect variables with annotation attribute '__OpenModelica_tearingSelect in [always, prefer, avoid, never]'
2410 1956 (tSel_always, tSel_prefer, tSel_avoid, tSel_never, tSel_alwaysByUser) := tearingSelect(var_lst, tearingSelect_always, DAEtypeStr);
2411
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1953 times.
1956 if not listEmpty(tSel_alwaysByUser) then
2412 12 Error.addMessage(Error.USER_TEARING_VARS, {intString(strongComponentIndex), BackendDump.printBackendDAEType2String(DAEtype), BackendDump.dumpMarkedVarList(var_lst, tSel_alwaysByUser)});
2413 end if;
2414 if debug then execStat("Tearing.CellierTearing -> 3"); end if;
2415
2416 // Initialize matching
2417 1956 ass1 := arrayCreate(size,-1);
2418 1956 ass2 := arrayCreate(size,-1);
2419 1956 order := {};
2420 if debug then execStat("Tearing.CellierTearing -> 3.1"); end if;
2421
2422
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1956 times.
1956 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2423 ✗ print("\n" + BORDER + "\nBEGINNING of CellierTearing2\n\n");
2424 end if;
2425 // ticket #15433: keep an unmutated copy of the enhanced transposed adjacency (variable ->
2426 // (equation, solvability)); CellierTearing2 consumes the adjacency matrices while matching, but
2427 // the structural-singularity test below needs the original incidence/solvability of every
2428 // variable. Only the initialization DAE can take the forcing path, so for the simulation DAE skip
2429 // the copy entirely (meTFull is then never read).
2430
2/2
✓ Branch 0 taken 930 times.
✓ Branch 1 taken 1026 times.
1956 meTFull := if forceDegree1 then arrayCopy(meT) else meT;
2431 // First pass: plain Cellier tearing, never forced.
2432 1956 (OutTVars, order) := CellierTearing2(false,m,mt,me,meT,ass1,ass2,unsolvables,{},discreteVars,tSel_always,tSel_prefer,tSel_avoid,tSel_never,order,mapEqnIncRow,mapIncRowEqn,eqnNonlinPoints,false);
2433
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1954 times.
1954 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2434 ✗ print("\nEND of CellierTearing2\n" + BORDER + "\n\n");
2435 end if;
2436
2437 // ticket #15433: only the cable's defect needs the degree-1 forcing. The equation-driven Cellier
2438 // pass matches on plain structural incidence, so a variable that is structurally incident to
2439 // several equations but solvable in only one (the cable's $START.x, solvable only in its defining
2440 // equation $START.x = sp) can be matched to an equation where it cannot be solved - a structurally
2441 // singular assignment that makes initialization fail. If this initialization component has a
2442 // $START.x->x cycle AND the plain matching contains such an unsolvable assignment, redo the
2443 // tearing with the variable-driven degree-1 forcing, which matches $START.x to its defining
2444 // equation and removes the singularity. A component whose plain matching is fully solvable - i.e.
2445 // one that already initializes correctly, like the OpenIPSL PSSE controllers - is left untouched.
2446
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1954 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1954 if forceDegree1 and hasStartCycle and tornMatchingIsStructurallySingular(ass2, meTFull, size, vars) then
2447 // CellierTearing2 consumed the adjacency matrices, so rebuild them before the forced re-run.
2448 ✗ subsyst := BackendDAEUtil.createEqSystem(vars, eqns);
2449 ✗ (subsyst,m,mt,_,_) := BackendDAEUtil.getAdjacencyMatrixScalar(subsyst, BackendDAE.NORMAL(),NONE(), BackendDAEUtil.isInitializationDAE(ishared));
2450 ✗ m := Array.map(m,deleteNegativeEntries);
2451 ✗ mt := Array.map(mt,deleteNegativeEntries);
2452 ✗ (me,meT,mapEqnIncRow,mapIncRowEqn) := BackendDAEUtil.getAdjacencyMatrixEnhancedScalar(subsyst,ishared,false);
2453 ✗ unsolvables := getUnsolvableVars(size,meT);
2454 ✗ eqnNonlinPoints := arrayCreate(size, -1);
2455 ✗ getEquationNonlinearityPoints(eqnNonlinPoints, me, size);
2456 ✗ ass1 := arrayCreate(size,-1);
2457 ✗ ass2 := arrayCreate(size,-1);
2458 ✗ order := {};
2459 ✗ (OutTVars, order) := CellierTearing2(false,m,mt,me,meT,ass1,ass2,unsolvables,{},discreteVars,tSel_always,tSel_prefer,tSel_avoid,tSel_never,order,mapEqnIncRow,mapIncRowEqn,eqnNonlinPoints,true);
2460 end if;
2461
2462 // check if tearing makes sense
2463 1954 tornsize := listLength(OutTVars);
2464 b := intLt(tornsize, size);
2465
2466 if debug then execStat("Tearing.CellierTearing -> 3.2"); end if;
2467 // Unassigned equations are residual equations
2468 1954 residual := getUnassigned(ass2);
2469 if debug then execStat("Tearing.CellierTearing -> 3.3"); end if;
2470 1954 residual_coll := List.map1r(residual,arrayGet,mapIncRowEqn);
2471 if debug then execStat("Tearing.CellierTearing -> 3.4"); end if;
2472 1954 residual_coll := List.unique(residual_coll);
2473 if debug then execStat("Tearing.CellierTearing -> 3.5"); end if;
2474 // dump results with local indexes
2475
3/4
✓ Branch 1 taken 1952 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1952 times.
1954 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2476 2 dumpTearingSetLocalIndexes(OutTVars,residual_coll,order,ass2,size,mapEqnIncRow,vars,eqns," - STRICT SET");
2477 end if;
2478
2479 if debug then execStat("Tearing.CellierTearing -> 4"); end if;
2480
2481 // Convert indexes
2482 1954 OutTVars := selectFromList_rev(vindx, OutTVars);
2483 1954 residual := selectFromList_rev(eindex, residual_coll);
2484
2485 // assign innerEquations:
2486 1954 innerEquations := assignInnerEquations(order,eindex,vindx,ass2,mapEqnIncRow,NONE());
2487 if debug then execStat("Tearing.CellierTearing -> 5"); end if;
2488
2489 // Create BackendDAE.TearingSet for strict set
2490 1954 strictTearingSet := BackendDAE.TEARINGSET(OutTVars,residual,innerEquations,BackendDAE.EMPTY_JACOBIAN());
2491
2492 // dump results with global indexes
2493
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1954 times.
1954 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2494 ✗ dumpTearingSetGlobalIndexes(strictTearingSet,size," - STRICT SET");
2495 end if;
2496
2497
2498 // Determine casual tearing set if dynamic tearing is enabled
2499 // *****************************************************************
2500
2501
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1951 times.
1954 if dynamicTearing then
2502
2503
3/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
3 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2504 1 print("\n\nDetermine CASUAL TEARING SET\n" + BORDER + BORDER + "\n\n");
2505 end if;
2506
2507 // Get adjacency matrix again
2508 3 (_,m,mt,_,_) := BackendDAEUtil.getAdjacencyMatrixScalar(subsyst, BackendDAE.NORMAL(),NONE(), BackendDAEUtil.isInitializationDAE(ishared));
2509
2510 // Delete negative entries from adjacency matrix
2511 3 m := Array.map(m,deleteNegativeEntries);
2512 3 mt := Array.map(mt,deleteNegativeEntries);
2513
2514 // Get advanced adjacency matrix (determine if the equations are solvable for the variables)
2515 3 (me,meT,mapEqnIncRow,mapIncRowEqn) := BackendDAEUtil.getAdjacencyMatrixEnhancedScalar(subsyst,ishared,true);
2516
2517 // Determine unsolvable vars to consider solvability
2518 3 unsolvables := getUnsolvableVars(size,meT);
2519
2520
3/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
3 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2521 1 print("\nAdjacencyMatrixEnhanced:\n");
2522 1 BackendDump.dumpAdjacencyMatrixEnhanced(me);
2523 1 print("\nAdjacencyMatrixTransposedEnhanced:\n");
2524 1 BackendDump.dumpAdjacencyMatrixTEnhanced(meT);
2525 1 print("\neqLinPoints:\n" + stringDelimitList(List.mapArray(eqnNonlinPoints, intString),",") + "\n\n");
2526 end if;
2527
2528
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2529 ✗ print("mapEqnIncRow:"); //+ stringDelimitList(List.map(List.flatten(arrayList(mapEqnIncRow)),intString),",") + "\n\n");
2530 ✗ BackendDump.dumpAdjacencyMatrix(mapEqnIncRow);
2531 ✗ print("\nmapIncRowEqn:\n" + stringDelimitList(List.mapArray(mapIncRowEqn, intString),",") + "\n\n");
2532 ✗ print("\n\nUNSOLVABLES:\n" + stringDelimitList(List.map(unsolvables,intString),",") + "\n\n");
2533 ✗ print("\nDiscrete Vars:\n" + stringDelimitList(List.map(discreteVars,intString),",") + "\n\n");
2534 end if;
2535
2536 // Initialize matching
2537 3 ass1 := arrayCreate(size,-1);
2538 3 ass2 := arrayCreate(size,-1);
2539 3 order := {};
2540
2541
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2542 ✗ print("\n" + BORDER + "\nBEGINNING of CellierTearing2\n\n");
2543 end if;
2544 // ticket #15433: the degree-1 forcing is only applied to the strict set (above) when its plain
2545 // pass is singular; never force the casual/dynamic set.
2546 3 (OutTVars, order) := CellierTearing2(false,m,mt,me,meT,ass1,ass2,unsolvables,{},discreteVars,tSel_always,tSel_prefer,tSel_avoid,tSel_never,order,mapEqnIncRow,mapIncRowEqn,eqnNonlinPoints,false);
2547
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2548 ✗ print("\nEND of CellierTearing2\n" + BORDER + "\n\n");
2549 end if;
2550
2551 // only continue if dynamic tearing makes sense (casual set < strict set)
2552
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if intLt(listLength(OutTVars), tornsize) then
2553
2554 // Unassigned equations are residual equations
2555 3 residual := getUnassigned(ass2);
2556 3 residual_coll := List.map1r(residual,arrayGet,mapIncRowEqn);
2557 3 residual_coll := List.unique(residual_coll);
2558
2559 // dump results with local indexes
2560
3/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
3 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2561 1 dumpTearingSetLocalIndexes(OutTVars,residual_coll,order,ass2,size,mapEqnIncRow,vars,eqns," - CASUAL SET");
2562 end if;
2563
2564 // Convert indexes
2565 3 OutTVars := selectFromList_rev(vindx, OutTVars);
2566 3 residual := selectFromList_rev(eindex, residual_coll);
2567
2568 // assign innerEquations:
2569 3 innerEquations := assignInnerEquations(order,eindex,vindx,ass2,mapEqnIncRow,SOME(me));
2570
2571 // Create BackendDAE.TearingSet for casual set
2572 3 casualTearingSet := SOME(BackendDAE.TEARINGSET(OutTVars,residual,innerEquations,BackendDAE.EMPTY_JACOBIAN()));
2573
2574 // dump results with global indexes
2575
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2576 ✗ dumpTearingSetGlobalIndexes(BackendDAE.TEARINGSET(OutTVars,residual,innerEquations,BackendDAE.EMPTY_JACOBIAN()),size," - CASUAL SET");
2577 end if;
2578
2579
3/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
3 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2580
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if linear then s:="Linear"; else s:="Nonlinear"; end if;
2581 1 print("\nNote:\n=====\n" + s + " dynamic tearing for this strong component in model:\n" + modelName + "\n\n");
2582 end if;
2583
2584 else
2585 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2586 ✗ print("\n" + BORDER + "\n* TEARING RESULTS (CASUAL SET):\n*\n* No of equations in strong component: "+intString(size)+"\n");
2587 ✗ print("* No of tVars: "+intString(listLength(OutTVars))+"\n");
2588 ✗ print("*\n* tVars: "+ stringDelimitList(List.map(listReverse(OutTVars),intString),",") + "\n");
2589 ✗ print("*\n* The casual tearing set is not smaller\n* than the strict tearing set and there-\n* fore it is discarded.\n*" + BORDER + "\n");
2590 end if;
2591
2592 ✗ if not b and not Flags.getConfigBool(Flags.FORCE_TEARING) then
2593 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2594 ✗ print("\nNote:\n=====\nTearing set is discarded because it is not smaller than the original set. Use +forceTearing to prevent this.\n\n");
2595 end if;
2596 ✗ fail();
2597 end if;
2598 casualTearingSet := NONE();
2599 end if;
2600 if debug then execStat("Tearing.CellierTearing -> 6"); end if;
2601
2602 else
2603
3/4
✓ Branch 1 taken 1950 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 1950 times.
1951 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2604 1 print("Note:\n=====\nNo dynamic Tearing for this strong component. Check if\n- flag 'dynamicTearing' is set proper\n- strong component does not contain statesets\n- system belongs to simulation\n- SimCode target is 'C'\n\n");
2605 end if;
2606
4/4
✓ Branch 0 taken 177 times.
✓ Branch 1 taken 1774 times.
✓ Branch 3 taken 175 times.
✓ Branch 4 taken 2 times.
1951 if not b and not Flags.getConfigBool(Flags.FORCE_TEARING) then
2607
2/4
✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 175 times.
175 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2608 ✗ print("\nNote:\n=====\nTearing set is discarded because it is not smaller than the original set. Use +forceTearing to prevent this.\n\n");
2609 end if;
2610 175 fail();
2611 end if;
2612 casualTearingSet := NONE();
2613 if debug then execStat("Tearing.CellierTearing -> 7"); end if;
2614 end if;
2615
2616 // Determine the rest of the information needed for BackendDAE.TORNSYSTEM
2617 // ***************************************************************************
2618
2619
4/4
✓ Branch 0 taken 1568 times.
✓ Branch 1 taken 211 times.
✓ Branch 2 taken 828 times.
✓ Branch 3 taken 951 times.
4175 ocomp := BackendDAE.TORNSYSTEM(strictTearingSet,casualTearingSet,linear,mixedSystem);
2620 outRunMatching := true;
2621
1/2
✓ Branch 1 taken 1779 times.
✗ Branch 2 not taken.
1779 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2622 ✗ print("\nEND of CellierTearing\n" + BORDER + "\n\n");
2623 end if;
2624 end CellierTearing;
2625
2626
2627 protected function tearingSelect
2628 "collects variables with annotation attribute '__OpenModelica_tearingSelect in [always, prefer, avoid, never]'
2629 author: ptaeuber FHB 2014-05"
2630 input list<BackendDAE.Var> var_lstIn;
2631 input output list<Integer> always;
2632 input String DAEtypeStr;
2633 output list<Integer> prefer = {};
2634 output list<Integer> avoid = {};
2635 output list<Integer> never = {};
2636 output list<Integer> alwaysByUser = always "distinguish between user choice and compiler choice";
2637 protected
2638 BackendDAE.Var var;
2639 Integer index = 1;
2640 Option<BackendDAE.TearingSelect> ts;
2641 Boolean preferTVarsWithStartValue;
2642 Boolean inSimulation = DAEtypeStr == "simulation";
2643 Boolean decided;
2644 algorithm
2645
4/6
✓ Branch 1 taken 1991 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 946 times.
✓ Branch 4 taken 1045 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 946 times.
1991 preferTVarsWithStartValue := Flags.getConfigBool(Flags.PREFER_TVARS_WITH_START_VALUE) and (DAEtypeStr == "initialization");
2646
2/2
✓ Branch 0 taken 36914 times.
✓ Branch 1 taken 1991 times.
38905 for var in var_lstIn loop
2647 // Get the value of the variable's __OpenModelica_tearingSelect attribute.
2648 36914 BackendDAE.VAR(tearingSelectOption = ts) := var;
2649
2650 // Add the variable's index to the appropriate list.
2651 decided := match ts
2652 case NONE() then false;
2653 case SOME(BackendDAE.ALWAYS()) algorithm
2654
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if not listMember(index, always) then
2655 always := index :: always;
2656 alwaysByUser := index :: alwaysByUser;
2657 end if;
2658 then true;
2659 case SOME(BackendDAE.PREFER()) algorithm prefer := index :: prefer; then true;
2660 case SOME(BackendDAE.DEFAULT()) then true;
2661 case SOME(BackendDAE.AVOID()) algorithm avoid := index :: avoid; then true;
2662 case SOME(BackendDAE.NEVER()) algorithm never := index :: never; then true;
2663 end match;
2664
2665
2/2
✓ Branch 0 taken 35779 times.
✓ Branch 1 taken 1135 times.
36914 if not decided then
2666 // During simulation, always select states
2667 // see https://github.com/OpenModelica/OpenModelica/issues/7704
2668
1/8
✗ Branch 1 not taken.
✓ Branch 2 taken 35779 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
35779 if Flags.getConfigBool(Flags.TEARING_ALWAYS_DERIVATIVES) and
2669 inSimulation and BackendVariable.isStateVar(var) and not listMember(index, always) then
2670 ✗ always := index :: always;
2671
2672 // Also prefer variables with start value that is not just the default of their type
2673 elseif preferTVarsWithStartValue and BackendVariable.varHasStartValue(var) and not BackendVariable.varStartFromType(var) then
2674 prefer := index :: prefer;
2675 end if;
2676 end if;
2677 36914 index := index + 1;
2678 end for;
2679
2680
1/2
✓ Branch 1 taken 1991 times.
✗ Branch 2 not taken.
1991 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2681 ✗ print("\nExternal influence on selection of iteration variables by variable annotations (__OpenModelica_tearingSelect)" + (if preferTVarsWithStartValue then " and preference of variables with start attribute" else "") + ":\n");
2682 ✗ print("Always: " + stringDelimitList(List.map(always, intString), ",") + "\n");
2683 ✗ print("Prefer: " + stringDelimitList(List.map(prefer, intString), ",")+ "\n");
2684 ✗ print("Avoid: " + stringDelimitList(List.map(avoid, intString), ",")+ "\n");
2685 ✗ print("Never: " + stringDelimitList(List.map(never, intString), ",") + "\n\n");
2686 end if;
2687 end tearingSelect;
2688
2689
2690 public function deleteNegativeEntries
2691 "deletes all negative entries from adjacency matrix, works with Array.map1, needed for proper Cellier-Tearing
2692 author: ptaeuber FHB 2014-01"
2693 input list<Integer> rowIn;
2694 output list<Integer> rowOut;
2695 algorithm
2696
6/6
✓ Branch 0 taken 2378 times.
✓ Branch 1 taken 284969 times.
✓ Branch 2 taken 287347 times.
✓ Branch 3 taken 73295 times.
✓ Branch 4 taken 284969 times.
✓ Branch 5 taken 73295 times.
360642 rowOut := list(r for r guard r > 0 in rowIn);
2697 end deleteNegativeEntries;
2698
2699
2700 protected function findDiscrete "takes a list of BackendDAE.Var and returns the indexes of the discrete Variables
2701 author: ptaeuber FHB 2014-01"
2702 input list<BackendDAE.Var> inVars;
2703 output list<Integer> discreteVarsOut = {};
2704 protected
2705 Integer index = 1;
2706 algorithm
2707
2/2
✓ Branch 0 taken 36637 times.
✓ Branch 1 taken 1957 times.
38594 for var in inVars loop
2708
2/2
✓ Branch 1 taken 360 times.
✓ Branch 2 taken 36277 times.
36637 if BackendVariable.isVarDiscrete(var) then
2709 discreteVarsOut := index::discreteVarsOut;
2710 end if;
2711 36637 index := index + 1;
2712 end for;
2713 end findDiscrete;
2714
2715 protected function findDiscreteWarnTearingSelect
2716 "mahge: Finds discrete variables in a variable list. This will also warn if the
2717 discrete variable has tearing select annotations. Right now this is used in manadatory
2718 tearing. It should probably used everywhere else as long as we don't have mixed solvers
2719 since we can not chose a discrete variable as a tearing(iteration) variable anyway.
2720 Note: This function returns indices based on the order of the variables in the list."
2721 input list<BackendDAE.Var> inVars;
2722 output list<Integer> discreteVarsOut = {};
2723 protected
2724 Integer index = 1;
2725 algorithm
2726
2/2
✓ Branch 0 taken 2226 times.
✓ Branch 1 taken 34 times.
2260 for var in inVars loop
2727
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2226 times.
2226 if BackendVariable.isVarDiscrete(var) then
2728 discreteVarsOut := index::discreteVarsOut;
2729
2730 () := match var.tearingSelectOption
2731 case SOME(BackendDAE.ALWAYS()) algorithm
2732 ✗ Error.addSourceMessage(Error.COMPILER_WARNING,{"Minimal Tearing is ignoring '__OpenModelica_tearingSelect = TearingSelect.always' annotation for discrete variable: "
2733 + BackendDump.varString(var)},ElementSource.getInfo(var.source));
2734 then ();
2735 case SOME(BackendDAE.PREFER()) algorithm
2736 ✗ Error.addSourceMessage(Error.COMPILER_WARNING,{"Minimal Tearing is ignoring '__OpenModelica_tearingSelect = TearingSelect.prefer' annotation for discrete variable: "
2737 + BackendDump.varString(var)},ElementSource.getInfo(var.source));
2738 then ();
2739 else ();
2740 end match;
2741
2742 end if;
2743 2226 index := index + 1;
2744 end for;
2745 end findDiscreteWarnTearingSelect;
2746
2747 protected function findCSE
2748 input list<BackendDAE.Var> inVars;
2749 output list<Integer> cseVarsOut = {};
2750 protected
2751 Integer index = 1;
2752 algorithm
2753
2/2
✓ Branch 0 taken 2226 times.
✓ Branch 1 taken 34 times.
2260 for var in inVars loop
2754
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2220 times.
2226 if BackendVariable.isCSEVar(var) then
2755 cseVarsOut := index::cseVarsOut;
2756 end if;
2757 2226 index := index + 1;
2758 end for;
2759 end findCSE;
2760
2761
2762 protected function getEquationNonlinearityPoints
2763 "Function returns an array with an integer assessment of the nonlinearity
2764 author: ptaeuber"
2765 input output array<Integer> eqnNonlinPoints;
2766 input BackendDAE.AdjacencyMatrixEnhanced me;
2767 input Integer size;
2768 protected
2769 BackendDAE.AdjacencyMatrixElementEnhanced row;
2770 Integer sum;
2771 algorithm
2772
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1956 times.
38589 for i in 1:size loop
2773 36633 row := me[i];
2774 sum := 0;
2775
2/2
✓ Branch 0 taken 143641 times.
✓ Branch 1 taken 36633 times.
180274 for entry in row loop
2776 143641 sum := sum + nonlinearityWeight(entry);
2777 end for;
2778 36633 eqnNonlinPoints[i] := sum;
2779 end for;
2780 end getEquationNonlinearityPoints;
2781
2782
2783 protected function nonlinearityWeight
2784 "Function returns a weight for specific solvability
2785 author: ptaeuber"
2786 input BackendDAE.AdjacencyMatrixElementEnhancedEntry entry;
2787 output Integer weight;
2788 algorithm
2789 weight := match entry
2790 case(_, BackendDAE.SOLVABILITY_SOLVED(), _) then 0;
2791 case(_, BackendDAE.SOLVABILITY_CONSTONE(), _) then 2;
2792 case(_, BackendDAE.SOLVABILITY_CONST(), _) then 5;
2793 case(_, BackendDAE.SOLVABILITY_PARAMETER(b=true), _) then 10;
2794 case(_, BackendDAE.SOLVABILITY_PARAMETER(b=false), _) then 20;
2795 case(_, BackendDAE.SOLVABILITY_LINEAR(b=true), _) then 20;
2796 case(_, BackendDAE.SOLVABILITY_LINEAR(b=false), _) then 50;
2797 case(_, BackendDAE.SOLVABILITY_NONLINEAR(), _) then 50;
2798 case(_, BackendDAE.SOLVABILITY_UNSOLVABLE(), _) then 100;
2799 else 0;
2800 end match;
2801 end nonlinearityWeight;
2802
2803
2804 protected function CellierTearing2 " function to call tearing heuristic and matching algorithm
2805 author: ptaeuber FHB 2013-2015"
2806 input Boolean inCausal;
2807 input BackendDAE.AdjacencyMatrix mIn;
2808 input BackendDAE.AdjacencyMatrixT mtIn;
2809 input BackendDAE.AdjacencyMatrixEnhanced meIn;
2810 input BackendDAE.AdjacencyMatrixTEnhanced meTIn;
2811 input array<Integer> ass1In,ass2In;
2812 input list<Integer> Unsolvables,tvarsIn,discreteVars,tSel_always,tSel_prefer,tSel_avoid,tSel_never,orderIn;
2813 input array<list<Integer>> mapEqnIncRow;
2814 input array<Integer> mapIncRowEqn;
2815 input array<Integer> eqnNonlinPoints;
2816 input Boolean forceDegree1 "ticket #15433: enable variable-driven degree-1 forced assignment (set by CellierTearing only for initialization components that have a $START.x->x cycle)";
2817 output list<Integer> OutTVars;
2818 output list<Integer> orderOut;
2819 protected
2820 constant Boolean debug = false;
2821 algorithm
2822
2/2
✓ Branch 0 taken 1957 times.
✓ Branch 1 taken 4105 times.
6062 if inCausal then
2823 OutTVars := tvarsIn;
2824 orderOut := orderIn;
2825 if debug then execStat("Tearing.CellierTearing2 - done"); end if;
2826
1/2
✓ Branch 0 taken 1957 times.
✗ Branch 1 not taken.
1957 return;
2827 end if;
2828 (OutTVars, orderOut) := match (Unsolvables,tSel_always)
2829 local
2830 Integer tvar;
2831 list<Integer> tvars,unsolvables,tVar_never,tVar_discrete,order;
2832 Boolean causal;
2833
2834 // case: There are no unsolvables and no variables with annotation '__OpenModelica_tearingSelect = TearingSelect.always'
2835 case ({},{})
2836 algorithm
2837 // select tearing Var
2838
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3314 times.
3314 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2839 ✗ print("\n" + BORDER + "\nBEGINNING of selectTearingVar\n\n");
2840 end if;
2841 3314 tvar := selectTearingVar(meIn,meTIn,mIn,mtIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
2842 if debug then execStat("Tearing.CellierTearing2 - 1.0"); end if;
2843
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3312 times.
3312 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2844 ✗ print("\nEND of selectTearingVar\n" + BORDER + "\n\n");
2845 end if;
2846
2847 // mark tvar in ass1In
2848 3312 arrayUpdate(ass1In,tvar,arrayLength(ass1In)*2);
2849
2850 // remove tearing var from adjacency matrix and transposed inc matrix
2851 3312 deleteEntriesFromAdjacencyMatrix(mIn,mtIn,{tvar});
2852
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3312 times.
3312 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2853 ✗ print("\n\n###BEGIN print Adjacency Matrix w/o tvar############\n(Function: CellierTearing2)\n");
2854 ✗ BackendDump.dumpAdjacencyMatrix(mIn);
2855 end if;
2856 3312 Array.replaceAtWithFill(tvar,{},{},mtIn);
2857
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3312 times.
3312 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2858 ✗ BackendDump.dumpAdjacencyMatrixT(mtIn);
2859 ✗ print("\n###END print Adjacency Matrix w/o tvar##############\n(Function: CellierTearing2)\n\n\n");
2860 end if;
2861
2862 if debug then execStat("Tearing.CellierTearing2 - 1.1"); end if;
2863 tvars := tvar::tvarsIn;
2864
2865 // assign vars to eqs until complete or partially causalisation(and restart algorithm)
2866
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3312 times.
3312 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2867 ✗ print("\n" + BORDER + "\nBEGINNING of TarjanMatching\n\n");
2868 end if;
2869 3312 (order,causal) := TarjanMatching(mIn,mtIn,meIn,meTIn,ass1In,ass2In,orderIn,mapEqnIncRow,mapIncRowEqn,eqnNonlinPoints,forceDegree1);
2870 if debug then execStat("Tearing.CellierTearing2 - 1.2"); end if;
2871
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3312 times.
3312 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2872 ✗ print("\nEND of TarjanMatching\n" + BORDER + "\n\n");
2873 ✗ print("\n" + BORDER + "\n* TARJAN RESULTS:\n* ass1: " + stringDelimitList(List.mapArray(ass1In, intString),",")+"\n");
2874 ✗ print("* ass2: "+stringDelimitList(List.mapArray(ass2In, intString),",")+"\n");
2875 ✗ print("* order: "+stringDelimitList(List.map(order,intString),",")+"\n" + BORDER + "\n\n");
2876 end if;
2877
5/6
✓ Branch 0 taken 1336 times.
✓ Branch 1 taken 1976 times.
✓ Branch 3 taken 1333 times.
✓ Branch 4 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1333 times.
3312 if causal and (Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE)) then
2878 3 print("\n");
2879 3 BackendDump.dumpMatching(ass1In);
2880 3 print("\norder: "+stringDelimitList(List.map(order,intString),",")+"\n" + UNDERLINE + "\n\n");
2881 end if;
2882
2883 // ascertain if there are new unsolvables now
2884 3312 unsolvables := getUnsolvableVarsConsiderMatching(arrayLength(meTIn),meTIn,ass1In,ass2In);
2885 if debug then execStat("Tearing.CellierTearing2 - 1.3"); end if;
2886 3312 (_,unsolvables,_) := List.intersection1OnTrue(unsolvables,tvars,intEq);
2887
2888 if debug then execStat("Tearing.CellierTearing2 - 1 done"); end if;
2889
2890 // repeat until system is causal
2891 4103 (tvars, order) := CellierTearing2(causal,mIn,mtIn,meIn,meTIn,ass1In,ass2In,unsolvables,tvars,discreteVars,tSel_always,tSel_prefer,tSel_avoid,tSel_never,order,mapEqnIncRow,mapIncRowEqn,eqnNonlinPoints,forceDegree1);
2892
2893 then
2894 (tvars,order);
2895
2896 // case: There are unsolvables and/or variables with annotation '__OpenModelica_tearingSelect = TearingSelect.always'
2897 else
2898 algorithm
2899 // First choose unsolvables and 'always'-vars as tVars
2900 791 tvars := List.unique(listAppend(Unsolvables,tSel_always));
2901 791 tVar_never := List.intersectionOnTrue(tSel_never,tvars,intEq);
2902 791 tVar_discrete := List.intersectionOnTrue(discreteVars,tvars,intEq);
2903
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 789 times.
791 if not listEmpty(tVar_never) then
2904 2 Error.addCompilerWarning("There are tearing variables with annotation attribute '__OpenModelica_tearingSelect = TearingSelect.never'. Use -d=tearingdump and -d=tearingdumpV for more information.");
2905 end if;
2906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 791 times.
791 if not listEmpty(tVar_discrete) then
2907 ✗ Error.addCompilerWarning("There are discrete tearing variables because otherwise the system could not have been torn (unsolvables). This may lead to problems during simulation.");
2908 end if;
2909
3/4
✓ Branch 1 taken 789 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 789 times.
791 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2910 2 print("\nForced selection of Tearing Variables:\n" + UNDERLINE + "\nUnsolvables as tVars: "+ stringDelimitList(List.map(Unsolvables,intString),",")+"\n");
2911 2 print("Variables with annotation attribute 'always' as tVars: "+ stringDelimitList(List.map(tSel_always,intString),",")+"\n");
2912 end if;
2913
2914 // mark tvars in ass1In
2915 791 markTVarsOrResiduals(tvars, ass1In);
2916
2917 // remove tearing var from adjacency matrix and transposed adjacency matrix
2918 791 deleteEntriesFromAdjacencyMatrix(mIn, mtIn, tvars);
2919 791 deleteRowsFromAdjacencyMatrix(mtIn, tvars);
2920
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 791 times.
791 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2921 ✗ print("\n\n###BEGIN print Adjacency Matrix w/o tvars###########\n(Function: CellierTearing2)\n");
2922 ✗ BackendDump.dumpAdjacencyMatrix(mIn);
2923 ✗ BackendDump.dumpAdjacencyMatrixT(mtIn);
2924 ✗ print("\n###END print Adjacency Matrix w/o tvars#############\n(Function: CellierTearing2)\n\n\n");
2925 ✗ print("\n" + BORDER + "\nBEGINNING of TarjanMatching\n\n");
2926 end if;
2927
2928 791 tvars := listAppend(tvars,tvarsIn) annotation(__OpenModelica_DisableListAppendWarning=true);
2929
2930 // assign vars to eqs until complete or partially causalisation(and restart algorithm)
2931 791 (order,causal) := TarjanMatching(mIn,mtIn,meIn,meTIn,ass1In,ass2In,orderIn,mapEqnIncRow,mapIncRowEqn,eqnNonlinPoints,forceDegree1);
2932
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 791 times.
791 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2933 ✗ print("\nEND of TarjanMatching\n" + BORDER + "\n\n");
2934 ✗ print("\n" + BORDER + "\n* TARJAN RESULTS:\n* ass1: " + stringDelimitList(List.mapArray(ass1In, intString),",")+"\n");
2935 ✗ print("* ass2: "+stringDelimitList(List.mapArray(ass2In,intString),",")+"\n");
2936 ✗ print("* order: "+stringDelimitList(List.map(order,intString),",")+"\n" + BORDER + "\n\n");
2937 end if;
2938
4/6
✓ Branch 0 taken 621 times.
✓ Branch 1 taken 170 times.
✓ Branch 3 taken 621 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 621 times.
791 if causal and (Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE)) then
2939 ✗ print("\n");
2940 ✗ BackendDump.dumpMatching(ass1In);
2941 ✗ print("\norder: "+stringDelimitList(List.map(order,intString),",")+"\n" + UNDERLINE + "\n\n");
2942 end if;
2943
2944 // ascertain if there are new unsolvables now
2945 791 unsolvables := getUnsolvableVarsConsiderMatching(arrayLength(meTIn),meTIn,ass1In,ass2In);
2946 791 (_,unsolvables,_) := List.intersection1OnTrue(unsolvables,tvars,intEq);
2947 if debug then execStat("Tearing.CellierTearing2 - 2"); end if;
2948
2949 // repeat until system is causal
2950 791 (tvars, order) := CellierTearing2(causal,mIn,mtIn,meIn,meTIn,ass1In,ass2In,unsolvables,tvars,discreteVars,{},tSel_prefer,tSel_avoid,tSel_never,order,mapEqnIncRow,mapIncRowEqn,eqnNonlinPoints,forceDegree1);
2951
2952 then
2953 (tvars, order);
2954 end match;
2955 end CellierTearing2;
2956
2957
2958 protected function selectTearingVar
2959 "Selects the next tearing variable referred to one of the following heuristics.
2960 author: ptaeuber FHB 2013-2015"
2961 input BackendDAE.AdjacencyMatrixEnhanced me;
2962 input BackendDAE.AdjacencyMatrixTEnhanced meT;
2963 input BackendDAE.AdjacencyMatrix m;
2964 input BackendDAE.AdjacencyMatrixT mt;
2965 input array<Integer> ass1In,ass2In;
2966 input list<Integer> discreteVars,tSel_prefer,tSel_avoid,tSel_never;
2967 input array<list<Integer>> mapEqnIncRow;
2968 input array<Integer> mapIncRowEqn;
2969 output Integer OutTVar;
2970 protected
2971 list<Integer> potentials;
2972 String heuristic;
2973 TearingHeuristic tearingHeuristic;
2974 algorithm
2975 // get the function for the requested tearing heuristic
2976 3314 heuristic := Config.getTearingHeuristic();
2977 tearingHeuristic := match heuristic
2978 case "MC1" then ModifiedCellierHeuristic_1;
2979 case "MC2" then ModifiedCellierHeuristic_2;
2980 case "MC11" then ModifiedCellierHeuristic_1_1;
2981 case "MC21" then ModifiedCellierHeuristic_2_1;
2982 case "MC12" then ModifiedCellierHeuristic_1_2;
2983 case "MC22" then ModifiedCellierHeuristic_2_2;
2984 case "MC13" then ModifiedCellierHeuristic_1_3;
2985 case "MC23" then ModifiedCellierHeuristic_2_3;
2986 case "MC231" then ModifiedCellierHeuristic_2_3_1;
2987 case "MC3" then ModifiedCellierHeuristic_3;
2988 case "MC4" then ModifiedCellierHeuristic_4;
2989 else
2990 algorithm
2991 ✗ Error.addInternalError("Unknown tearing heuristic: " + heuristic, sourceInfo());
2992 ✗ then fail();
2993 end match;
2994
2995
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3314 times.
3314 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
2996 ✗ print("\n" + BORDER + "\nBEGINNING of TearingHeuristic\n\n");
2997 ✗ print("Chosen Heuristic: " + heuristic + "\n\n\n");
2998 end if;
2999
3000 // get potential tearing variables
3001 try
3002
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3314 times.
3314 potentials := tearingHeuristic(m,mt,me,meT,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3003
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3312 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3312 times.
3314 {OutTVar} := potentials;
3004 else
3005 2 print("\nThe selection of a new tearing variable failed.\n");
3006 2 Error.addCompilerWarning("Function Tearing.selectTearingVar failed at least once. Use -d=tearingdump or -d=tearingdumpV for more information.");
3007 2 fail();
3008 end try;
3009
3010
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3312 times.
3312 if listMember(OutTVar,tSel_avoid) then
3011 ✗ Error.addCompilerWarning("The Tearing heuristic has chosen variables with annotation attribute '__OpenModelica_tearingSelect = TearingSelect.avoid'. Use -d=tearingdump and -d=tearingdumpV for more information.");
3012 end if;
3013
1/2
✓ Branch 1 taken 3312 times.
✗ Branch 2 not taken.
3312 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3014 ✗ print("\nEND of TearingHeuristic\n" + BORDER + "\n\n");
3015 end if;
3016 end selectTearingVar;
3017
3018
3019 protected partial function TearingHeuristic "Heuristic to find a preferably good tearing variable; interface function"
3020 input BackendDAE.AdjacencyMatrix mIn,mtIn;
3021 input BackendDAE.AdjacencyMatrixEnhanced meIn;
3022 input BackendDAE.AdjacencyMatrixTEnhanced metIn;
3023 input array<Integer> ass1In;
3024 input array<Integer> ass2In;
3025 input list<Integer> discreteVars;
3026 input list<Integer> tSel_prefer;
3027 input list<Integer> tSel_avoid;
3028 input list<Integer> tSel_never;
3029 input array<list<Integer>> mapEqnIncRow;
3030 input array<Integer> mapIncRowEqn;
3031 output list<Integer> potentials={};
3032 end TearingHeuristic;
3033
3034
3035 protected function ModifiedCellierHeuristic_1 " Heuristic to find a preferably good tearing variable [MC1].
3036 author: ptaeuber FHB 2013-2015"
3037 extends TearingHeuristic;
3038 protected
3039 Integer edges;
3040 list<Integer> selectedcols1,selectedrows;
3041 algorithm
3042 // Cellier heuristic [MC1]
3043
3044 // 0. get all unassigned variables
3045 146 selectedcols1 := getUnassigned(ass1In);
3046
3047 // 1. choose rows(eqs) with most nonzero entries and write the column indexes(vars) for nonzeros in a list
3048 146 selectedcols1 := getVarsOfEqnsWithMostVars(selectedcols1, mIn, mtIn);
3049
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 146 times.
146 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3050 ✗ print("1st: " + stringDelimitList(List.map(selectedcols1,intString),",") + "\n");
3051 end if;
3052 // Without discrete:
3053 146 (_,selectedcols1,_) := List.intersection1OnTrue(selectedcols1,discreteVars,intEq);
3054
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 146 times.
146 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3055 ✗ print("Without Discrete: " + stringDelimitList(List.map(selectedcols1,intString),",") + "\n(Variables in the equation(s) with most Variables)\n\n");
3056 end if;
3057
3058 // 2. choose rows (vars) with most nonzero entries and write the indexes in a list
3059 146 (edges,selectedcols1) := getVarsOccurringInMostEquations(mtIn, selectedcols1);
3060
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 146 times.
146 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3061 ✗ print("2nd: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Variables from (1st) with most occurrence in equations (" + intString(edges) +" times))\n\n");
3062 end if;
3063
3064 // 3. select the rows(eqs) from mIn which could be causalized by knowing one more Var
3065 146 selectedrows := traverseSingleEqnsforAssignable(ass2In,mIn,mapEqnIncRow,mapIncRowEqn);
3066
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 146 times.
146 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3067 ✗ print(stringDelimitList(List.map(selectedrows,intString),",")+"\n(Equations which could be causalized by knowing one more Var)\n\n");
3068 end if;
3069
3070 // 4. determine which possible Vars causalize most equations considering impossible assignments and write them into potentials
3071 146 (potentials,edges) := selectOneMostCausalizingVar(mtIn,selectedcols1, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(selectedrows, arrayLength(ass1In)));
3072
1/2
✓ Branch 1 taken 146 times.
✗ Branch 2 not taken.
146 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3073 ✗ print("\n3rd: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Chosen tearing variable. One from (2nd) causalizing most equations [" + intString(edges) + "])\n\n");
3074 end if;
3075 end ModifiedCellierHeuristic_1;
3076
3077
3078 protected function ModifiedCellierHeuristic_2 " Heuristic to find a preferably good tearing variable [MC2].
3079 author: ptaeuber FHB 2013-2015"
3080 extends TearingHeuristic;
3081 protected
3082 Integer edges;
3083 list<Integer> varlst,selectedcols1,selectedrows;
3084 algorithm
3085 // modified Cellier heuristic [MC2]
3086
3087 // 0. Consider only non-discrete Vars
3088 ✗ varlst := getUnassigned(ass1In);
3089 ✗ (_,selectedcols1,_) := List.intersection1OnTrue(varlst,discreteVars,intEq);
3090
3091 // 1. choose rows (vars) with most nonzero entries and write the indexes in a list
3092 ✗ (edges,selectedcols1) := getVarsOccurringInMostEquations(mtIn, selectedcols1);
3093 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3094 ✗ print("1st: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Non-discrete variables with most occurrence in equations (" + intString(edges) +" times))\n\n");
3095 end if;
3096
3097 // 2. select the rows(eqs) from mIn which could be causalized by knowing one more Var
3098 ✗ selectedrows := traverseSingleEqnsforAssignable(ass2In,mIn,mapEqnIncRow,mapIncRowEqn);
3099 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3100 ✗ print(stringDelimitList(List.map(selectedrows,intString),",")+"\n(Equations which could be causalized by knowing one more Var)\n\n");
3101 end if;
3102
3103 // 3. determine which possible Vars causalize most equations considering impossible assignments and write them into potentials
3104 ✗ (potentials,edges) := selectOneMostCausalizingVar(mtIn,selectedcols1, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(selectedrows, arrayLength(ass1In)));
3105 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3106 ✗ print("\n2nd: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Chosen tearing variable. One from (1st) causalizing most equations [" + intString(edges) + "])\n\n");
3107 end if;
3108 end ModifiedCellierHeuristic_2;
3109
3110
3111 protected function ModifiedCellierHeuristic_1_1 " Heuristic to find a preferably good tearing variable [MC11].
3112 author: ptaeuber FHB 2013-2015"
3113 extends TearingHeuristic;
3114 protected
3115 Integer edges;
3116 list<Integer> selectedcols1,selectedrows;
3117 algorithm
3118 // modified Cellier heuristic [MC11]
3119
3120 // 0. get all unassigned variables
3121 ✗ selectedcols1 := getUnassigned(ass1In);
3122
3123 // 1. choose rows(eqs) with most nonzero entries and write the column indexes(vars) for nonzeros in a list
3124 ✗ selectedcols1 := getVarsOfEqnsWithMostVars(selectedcols1, mIn, mtIn);
3125 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3126 ✗ print("1st: " + stringDelimitList(List.map(selectedcols1,intString),",") + "\n");
3127 end if;
3128
3129 // Without discrete:
3130 ✗ (_,selectedcols1,_) := List.intersection1OnTrue(selectedcols1,discreteVars,intEq);
3131 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3132 ✗ print("Without Discrete: " + stringDelimitList(List.map(selectedcols1,intString),",") + "\n(Variables in the equation(s) with most Variables)\n\n");
3133 end if;
3134
3135 // 2. choose rows (vars) with most nonzero entries and write the indexes in a list
3136 ✗ (edges,selectedcols1) := getVarsOccurringInMostEquations(mtIn, selectedcols1);
3137 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3138 ✗ print("2nd: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Variables from (1st) with most occurrence in equations (" + intString(edges) +" times))\n\n");
3139 end if;
3140
3141 // 3. select the rows(eqs) from mIn which could be causalized by knowing one more Var
3142 ✗ selectedrows := traverseSingleEqnsforAssignable(ass2In,mIn,mapEqnIncRow,mapIncRowEqn);
3143 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3144 ✗ print(stringDelimitList(List.map(selectedrows,intString),",")+"\n(Equations which could be causalized by knowing one more Var)\n\n");
3145 end if;
3146
3147 // 4. determine which possible Vars causalize most equations considering impossible assignments and write them into potentials
3148 ✗ (potentials,_) := selectMostCausalizingVars(mtIn,selectedcols1, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(selectedrows, arrayLength(ass1In)));
3149 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3150 ✗ print("\n3rd: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Variables from (2nd) causalizing most equations)\n\n");
3151 end if;
3152
3153 // 5. choose vars with the most impossible assignments
3154 ✗ (potentials,edges) := getOneVarWithMostImpAss(potentials,ass2In,metIn);
3155 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3156 ✗ print("\n4th: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Chosen tearing variable. One from from (3rd) with most incident impossible assignments [" + intString(edges) + "])\n\n");
3157 end if;
3158 end ModifiedCellierHeuristic_1_1;
3159
3160
3161 protected function ModifiedCellierHeuristic_2_1 " Heuristic to find a preferably good tearing variable [MC21].
3162 author: ptaeuber FHB 2013-2015"
3163 extends TearingHeuristic;
3164 protected
3165 Integer edges;
3166 list<Integer> varlst,selectedcols1,selectedrows;
3167 algorithm
3168 // modified Cellier heuristic [MC21]
3169
3170 // 0. Consider only non-discrete Vars
3171 2 varlst := getUnassigned(ass1In);
3172 2 (_,selectedcols1,_) := List.intersection1OnTrue(varlst,discreteVars,intEq);
3173
3174 // 1. choose rows (vars) with most nonzero entries and write the indexes in a list
3175 2 (edges,selectedcols1) := getVarsOccurringInMostEquations(mtIn, selectedcols1);
3176
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3177 ✗ print("1st: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Non-discrete variables with most occurrence in equations (" + intString(edges) +" times))\n\n");
3178 end if;
3179
3180 // 2. select the rows(eqs) from mIn which could be causalized by knowing one more Var
3181 2 selectedrows := traverseSingleEqnsforAssignable(ass2In,mIn,mapEqnIncRow,mapIncRowEqn);
3182
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3183 ✗ print(stringDelimitList(List.map(selectedrows,intString),",")+"\n(Equations which could be causalized by knowing one more Var)\n\n");
3184 end if;
3185
3186 // 3. determine which possible Vars causalize most equations considering impossible assignments and write them into potentials
3187 2 (potentials,_) := selectMostCausalizingVars(mtIn,selectedcols1, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(selectedrows, arrayLength(ass1In)));
3188
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3189 ✗ print("\n2nd: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Variables from (1st) causalizing most equations)\n\n");
3190 end if;
3191
3192 // 4. choose vars with the most impossible assignments
3193 2 (potentials,edges) := getOneVarWithMostImpAss(potentials,ass2In,metIn);
3194
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3195 ✗ print("\n3rd: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Chosen tearing variable. One from (2nd) with most incident impossible assignments [" + intString(edges) + "])\n\n");
3196 end if;
3197 end ModifiedCellierHeuristic_2_1;
3198
3199
3200 protected function ModifiedCellierHeuristic_1_2 " Heuristic to find a preferably good tearing variable [MC12].
3201 author: ptaeuber FHB 2013-2015"
3202 extends TearingHeuristic;
3203 protected
3204 Integer edges;
3205 list<Integer> selectedcols1,selectedrows;
3206 algorithm
3207 // modified Cellier heuristic [MC12]
3208
3209 // 0. get all unassigned variables
3210 2 selectedcols1 := getUnassigned(ass1In);
3211
3212 // 1. choose rows(eqs) with most nonzero entries and write the column indexes(vars) for nonzeros in a list
3213 2 selectedcols1 := getVarsOfEqnsWithMostVars(selectedcols1, mIn, mtIn);
3214
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3215 ✗ print("1st: " + stringDelimitList(List.map(selectedcols1,intString),",") + "\n");
3216 end if;
3217
3218 // Without discrete:
3219 2 (_,selectedcols1,_) := List.intersection1OnTrue(selectedcols1,discreteVars,intEq);
3220
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3221 ✗ print("Without Discrete: " + stringDelimitList(List.map(selectedcols1,intString),",") + "\n(Variables in the equation(s) with most Variables)\n\n");
3222 end if;
3223
3224 // 2. choose rows (vars) with most nonzero entries and write the indexes in a list
3225 2 (edges,selectedcols1) := getVarsOccurringInMostEquations(mtIn, selectedcols1);
3226
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3227 ✗ print("2nd: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Variables from (1st) with most occurrence in equations (" + intString(edges) +" times))\n\n");
3228 end if;
3229
3230 // 3. choose vars with the most impossible assignments
3231 2 (selectedcols1,_,_) := getAllVarsWithMostImpAss(selectedcols1,ass2In,metIn);
3232
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3233 ✗ print("\n3rd: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Variables from (2nd) with most incident impossible assignments)\n\n");
3234 end if;
3235
3236 // 4. select the rows(eqs) from mIn which could be causalized by knowing one more Var
3237 2 selectedrows := traverseSingleEqnsforAssignable(ass2In,mIn,mapEqnIncRow,mapIncRowEqn);
3238
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3239 ✗ print(stringDelimitList(List.map(selectedrows,intString),",")+"\n(Equations which could be causalized by knowing one more Var)\n\n");
3240 end if;
3241
3242 // 5. determine which possible Vars causalize most equations considering impossible assignments and write them into potentials
3243 2 (potentials,edges) := selectOneMostCausalizingVar(mtIn,selectedcols1, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(selectedrows, arrayLength(ass1In)));
3244
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3245 ✗ print("\n4th: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Chosen tearing variable.One from (3rd) causalizing most equations [" + intString(edges) + "])\n\n");
3246 end if;
3247 end ModifiedCellierHeuristic_1_2;
3248
3249
3250 protected function ModifiedCellierHeuristic_2_2 " Heuristic to find a preferably good tearing variable [MC22].
3251 author: ptaeuber FHB 2013-2015"
3252 extends TearingHeuristic;
3253 protected
3254 Integer edges;
3255 list<Integer> varlst,selectedcols1,selectedrows;
3256 algorithm
3257 // modified Cellier heuristic [MC22]
3258
3259 // 0. Consider only non-discrete Vars
3260 ✗ varlst := getUnassigned(ass1In);
3261 ✗ (_,selectedcols1,_) := List.intersection1OnTrue(varlst,discreteVars,intEq);
3262
3263 // 1. choose rows (vars) with most nonzero entries and write the indexes in a list
3264 ✗ (edges,selectedcols1) := getVarsOccurringInMostEquations(mtIn, selectedcols1);
3265 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3266 ✗ print("1st: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Non-discrete variables with most occurrence in equations (" + intString(edges) +" times))\n\n");
3267 end if;
3268
3269 // 2. choose vars with the most impossible assignments
3270 ✗ (selectedcols1,_,_) := getAllVarsWithMostImpAss(selectedcols1,ass2In,metIn);
3271 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3272 ✗ print("\n2nd: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Variables from (1st) with most incident impossible assignments)\n\n");
3273 end if;
3274
3275 // 3. select the rows(eqs) from mIn which could be causalized by knowing one more Var
3276 ✗ selectedrows := traverseSingleEqnsforAssignable(ass2In,mIn,mapEqnIncRow,mapIncRowEqn);
3277 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3278 ✗ print(stringDelimitList(List.map(selectedrows,intString),",")+"\n(Equations which could be causalized by knowing one more Var)\n\n");
3279 end if;
3280
3281 // 4. determine which possible Vars causalize most equations considering impossible assignments and write them into potentials
3282 ✗ (potentials,edges) := selectOneMostCausalizingVar(mtIn,selectedcols1, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(selectedrows, arrayLength(ass1In)));
3283 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3284 ✗ print("\n3rd: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Chosen tearing variable. One from (2nd) causalizing most equations [" + intString(edges) + "])\n\n");
3285 end if;
3286 end ModifiedCellierHeuristic_2_2;
3287
3288
3289 protected function ModifiedCellierHeuristic_1_3 " Heuristic to find a preferably good tearing variable [MC13].
3290 author: ptaeuber FHB 2013-2015"
3291 extends TearingHeuristic;
3292 protected
3293 Integer edges,maxPoints;
3294 list<Integer> selectedcols1,selectedrows,points,counts1,counts2;
3295 algorithm
3296 // Cellier heuristic [MC13]
3297
3298 // 0. get all unassigned variables
3299 ✗ selectedcols1 := getUnassigned(ass1In);
3300
3301 // 1. choose rows(eqs) with most nonzero entries and write the column indexes(vars) for nonzeros in a list
3302 ✗ selectedcols1 := getVarsOfEqnsWithMostVars(selectedcols1, mIn, mtIn);
3303 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3304 ✗ print("1st: " + stringDelimitList(List.map(selectedcols1,intString),",") + "\n");
3305 end if;
3306
3307 // Without discrete:
3308 ✗ (_,selectedcols1,_) := List.intersection1OnTrue(selectedcols1,discreteVars,intEq);
3309 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3310 ✗ print("Without Discrete: " + stringDelimitList(List.map(selectedcols1,intString),",") + "\n(Variables in the equation(s) with most Variables)\n\n");
3311 end if;
3312
3313 // 2. choose rows (vars) with most nonzero entries and write the indexes in a list
3314 ✗ (edges,selectedcols1) := getVarsOccurringInMostEquations(mtIn, selectedcols1);
3315 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3316 ✗ print("2nd: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Variables from (1st) with most occurrence in equations (" + intString(edges) +" times))\n\n");
3317 end if;
3318
3319 // 3. select the rows(eqs) from mIn which could be causalized by knowing one more Var
3320 ✗ selectedrows := traverseSingleEqnsforAssignable(ass2In,mIn,mapEqnIncRow,mapIncRowEqn);
3321 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3322 ✗ print(stringDelimitList(List.map(selectedrows,intString),",")+"\n(Equations which could be causalized by knowing one more Var)\n\n");
3323 end if;
3324
3325 // 4. determine for each variable the number of equations it could causalize considering impossible assignments and save them in counts1
3326 ✗ (_,counts1) := selectMostCausalizingVars(mtIn,selectedcols1, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(selectedrows, arrayLength(ass1In)));
3327 ✗ counts1 := listReverse(counts1);
3328
3329 // 5. determine for each variable the number of impossible assignments and save them in counts2
3330 ✗ (_,counts2,_) := getAllVarsWithMostImpAss(selectedcols1,ass2In,metIn);
3331
3332 // 6. Calculate the sum of number of impossible assignments and causalizable equations for each variable and save them in points
3333 ✗ points := List.threadMap(counts1,counts2,intAdd);
3334 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3335 ✗ print("\nPoints: "+ stringDelimitList(List.map(points,intString),",")+"\n(Sum of impossible assignments and causalizable equations)\n");
3336 end if;
3337
3338 // 7. Choose vars with most points as potentials (tearing variable)
3339 ✗ (potentials, maxPoints) := getOneVarWithMostPoints(selectedcols1, points);
3340 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3341 ✗ print("\n3rd: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Chosen tearing variable. One from (2nd) with most points [" + intString(maxPoints) + "])\n\n");
3342 end if;
3343 end ModifiedCellierHeuristic_1_3;
3344
3345
3346 protected function ModifiedCellierHeuristic_2_3 " Heuristic to find a preferably good tearing variable [MC23].
3347 author: ptaeuber FHB 2013-2015"
3348 extends TearingHeuristic;
3349 protected
3350 Integer edges,maxPoints;
3351 list<Integer> varlst,selectedcols1,selectedrows,points,counts1,counts2;
3352 algorithm
3353 // Cellier heuristic [MC23]
3354
3355 // 0. Consider only non-discrete Vars
3356 2 varlst := getUnassigned(ass1In);
3357 2 (_,selectedcols1,_) := List.intersection1OnTrue(varlst,discreteVars,intEq);
3358
3359 // 1. choose rows (vars) with most nonzero entries and write the indexes in a list
3360 2 (edges,selectedcols1) := getVarsOccurringInMostEquations(mtIn, selectedcols1);
3361
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3362 ✗ print("1st: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Non-discrete variables with most occurrence in equations (" + intString(edges) +" times))\n\n");
3363 end if;
3364
3365 // 2. select the rows(eqs) from mIn which could be causalized by knowing one more Var
3366 2 selectedrows := traverseSingleEqnsforAssignable(ass2In,mIn,mapEqnIncRow,mapIncRowEqn);
3367
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3368 ✗ print(stringDelimitList(List.map(selectedrows,intString),",")+"\n(Equations which could be causalized by knowing one more Var)\n\n");
3369 end if;
3370
3371 // 3. determine for each variable the number of equations it could causalize considering impossible assignments and save them in counts1
3372 2 (_,counts1) := selectMostCausalizingVars(mtIn,selectedcols1, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(selectedrows, arrayLength(ass1In)));
3373 2 counts1 := listReverse(counts1);
3374
3375 // 4. determine for each variable the number of impossible assignments and save them in counts2
3376 2 (_,counts2,_) := getAllVarsWithMostImpAss(selectedcols1,ass2In,metIn);
3377
3378 // 5. Calculate the sum of number of impossible assignments and causalizable equations for each variable and save them in points
3379 2 points := List.threadMap(counts1,counts2,intAdd);
3380
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3381 ✗ print("\nPoints: "+ stringDelimitList(List.map(points,intString),",")+"\n(Sum of impossible assignments and causalizable equations)\n");
3382 end if;
3383
3384 // 6. Choose vars with most points as potentials (tearing variable)
3385 2 (potentials, maxPoints) := getOneVarWithMostPoints(selectedcols1, points);
3386
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3387 ✗ print("\n2nd: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Chosen tearing variable. One from (1st) with most points [" + intString(maxPoints) + "])\n\n");
3388 end if;
3389 end ModifiedCellierHeuristic_2_3;
3390
3391
3392 protected function ModifiedCellierHeuristic_2_3_1 " Heuristic to find a preferably good tearing variable [MC231].
3393 author: ptaeuber FHB 2013-2015"
3394 extends TearingHeuristic;
3395 protected
3396 Integer edges,potpoints1,potpoints2;
3397 list<Integer> varlst,selectedcols0,selectedcols1,selectedrows,potentials1,potentials2,counts1,counts2,points1,points2;
3398 algorithm
3399 // modified Cellier heuristic [MC231]
3400
3401 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3402 ✗ print("Start round 1:\n==============\n\n");
3403 end if;
3404
3405 // 0. Consider only non-discrete Vars
3406 ✗ varlst := getUnassigned(ass1In);
3407 ✗ (_,selectedcols0,_) := List.intersection1OnTrue(varlst,discreteVars,intEq);
3408
3409 // 1. choose rows (vars) with most nonzero entries and write the indexes in a list
3410 ✗ (edges,selectedcols1) := getVarsOccurringInMostEquations(mtIn, selectedcols0);
3411 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3412 ✗ print("1st: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Non-discrete variables with most occurrence in equations (" + intString(edges) +" times))\n\n");
3413 end if;
3414
3415 // 2. select the rows(eqs) from mIn which could be causalized by knowing one more Var
3416 ✗ selectedrows := traverseSingleEqnsforAssignable(ass2In,mIn,mapEqnIncRow,mapIncRowEqn);
3417 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3418 ✗ print(stringDelimitList(List.map(selectedrows,intString),",")+"\n(Equations which could be causalized by knowing one more Var)\n\n");
3419 end if;
3420
3421 // 3. determine for each variable the number of equations it could causalize considering impossible assignments and save them in counts1
3422 ✗ (_,counts1) := selectMostCausalizingVars(mtIn,selectedcols1, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(selectedrows, arrayLength(ass1In)));
3423 ✗ counts1 := listReverse(counts1);
3424
3425 // 4. determine for each variable the number of impossible assignments and save them in counts2
3426 ✗ (_,counts2,_) := getAllVarsWithMostImpAss(selectedcols1,ass2In,metIn);
3427
3428 // 5. Calculate the sum of number of impossible assignments and causalizable equations for each variable and save them in points1
3429 ✗ points1 := List.threadMap(counts1,counts2,intAdd);
3430 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3431 ✗ print("\nPoints: "+ stringDelimitList(List.map(points1,intString),",")+"\n(Sum of impossible assignments and causalizable equations)\n");
3432 end if;
3433
3434 // 6. Choose vars with most points as potentials (tearing variable)
3435 ✗ (potentials1, potpoints1) := getOneVarWithMostPoints(selectedcols1, points1);
3436 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3437 ✗ print("\n2nd: "+ stringDelimitList(List.map(potentials1,intString),",")+"\n(Chosen tearing variable. One from (1st) with most points (" + intString(potpoints1) + " points))\n\n");
3438 end if;
3439
3440 // 7. choose non-discrete vars with edges-1 edges and write the indexes in a list
3441 ✗ selectedcols1 := findNEntries(mtIn,selectedcols0, edges-1);
3442
3443 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3444 ✗ print("\nStart round 2:\n==============\n\n1st: "+ stringDelimitList(List.map(selectedcols1,intString),",")+"\n(Variables with occurrence in " + intString(edges-1) + " equations)\n\n" + stringDelimitList(List.map(selectedrows,intString),",")+"\n(Equations which could be causalized by knowing one more Var)\n\n");
3445 end if;
3446 ✗ if listEmpty(selectedcols1) then
3447 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3448 ✗ print("Second set is empty.");
3449 end if;
3450 potentials := potentials1;
3451 ✗ potpoints2 := 0;
3452 else
3453 // 9. determine for each variable the number of equations it could causalize considering impossible assignments and save them in counts1
3454 ✗ (_,counts1) := selectMostCausalizingVars(mtIn,selectedcols1, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(selectedrows, arrayLength(ass1In)));
3455 ✗ counts1 := listReverse(counts1);
3456
3457 // 10. determine for each variable the number of impossible assignments and save them in counts2
3458 ✗ (_,counts2,_) := getAllVarsWithMostImpAss(selectedcols1,ass2In,metIn);
3459
3460 // 11. Calculate the sum of number of impossible assignments and causalizable equations for each variable and save them in points2
3461 ✗ points2 := List.threadMap(counts1,counts2,intAdd);
3462 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3463 ✗ print("\nPoints: "+ stringDelimitList(List.map(points2,intString),",")+"\n(Sum of impossible assignments and causalizable equations)\n");
3464 end if;
3465
3466 // 12. Choose vars with most points as potentials (tearing variable)
3467 ✗ (potentials2, potpoints2) := getOneVarWithMostPoints(selectedcols1, points2);
3468 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3469 ✗ print("\n2nd: "+ stringDelimitList(List.map(potentials2,intString),",")+"\n(Chosen tearing variable. One from (1st) with most points (" + intString(potpoints2) + " points))\n\n");
3470 end if;
3471
3472 // 13. choose potentials-set with most points
3473 ✗ potentials := if intGe(potpoints1,potpoints2) then potentials1 else potentials2;
3474 end if;
3475 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3476 ✗ print("\n=====================\nChosen tearing variable: " + stringDelimitList(List.map(potentials,intString),",") + "\n=====================\n(from round 1: " + boolString(intGe(potpoints1,potpoints2)) + ")\n\n");
3477 end if;
3478 end ModifiedCellierHeuristic_2_3_1;
3479
3480
3481 protected function ModifiedCellierHeuristic_3 " Heuristic to find a preferably good tearing variable [MC3].
3482 author: ptaeuber FHB 2013-2015"
3483 extends TearingHeuristic;
3484 protected
3485 Integer edges,maxPoints;
3486 list<Integer> potentialTVars,potentialTVars2,bestPotentialTVars,causEq,points,counts1,counts2;
3487 constant Boolean debug = false;
3488 algorithm
3489 // Cellier heuristic [MC3]
3490 if debug then execStat("TEARINGHEURISTIC0"); end if;
3491
3492 // 1. Determine the equations with size(equation)+1 variables and save them in causEq
3493 // **********************************************************************************
3494 3162 causEq := traverseSingleEqnsforAssignable(ass2In,mIn,mapEqnIncRow,mapIncRowEqn);
3495
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3162 times.
3162 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3496 ✗ print("1st: "+ stringDelimitList(List.map(causEq,intString),",")+"\n(Equations which could be causalized by knowing one more variable)\n\n");
3497 end if;
3498 if debug then execStat("TEARINGHEURISTIC1"); end if;
3499
3500 // 2. Get all unassigned variables as a first selection of potential tearing variables
3501 // ***********************************************************************************
3502 3162 potentialTVars := getUnassigned(ass1In);
3503
3504
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3162 times.
3162 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3505 ✗ print("2nd: "+ stringDelimitList(List.map(potentialTVars,intString),",")+"\n(All unassigned variables)\n\n");
3506 end if;
3507 if debug then execStat("TEARINGHEURISTIC2"); end if;
3508
3509 // 3. Remove variables we don't want as tearing variables
3510 // ******************************************************
3511 // Remove variables with attribute '__OpenModelica_tearingSelect = TearingSelect.never'
3512 3162 (_,potentialTVars,_) := List.intersection1OnTrue(potentialTVars,tSel_never,intEq);
3513
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3160 times.
3162 if listEmpty(potentialTVars) then
3514 2 Error.addCompilerError("It is not possible to select a new tearing variable, because all remaining variables have the attribute '__OpenModelica_tearingSelect = TearingSelect.never'.");
3515 2 return;
3516 end if;
3517
3518 // Remove discrete variables
3519 3160 (_,potentialTVars2,_) := List.intersection1OnTrue(potentialTVars,discreteVars,intEq);
3520
3521 // Only discrete potentials, then allow discrete tearing variables
3522
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3158 times.
3160 if listEmpty(potentialTVars2) then
3523 2 potentialTVars2 := potentialTVars;
3524 2 Error.addCompilerWarning("The tearing heuristic was not able to avoid discrete iteration variables because otherwise the system could not have been torn. This may lead to problems during simulation.");
3525
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3526 ✗ print("3rd: "+ stringDelimitList(List.map(potentialTVars2,intString),",")+"\n(All unassigned variables without attribute 'never' (only discrete variables left))\n\n");
3527 end if;
3528 else
3529
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3158 times.
3158 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3530 ✗ print("3rd: "+ stringDelimitList(List.map(potentialTVars2,intString),",")+"\n(All non-discrete variables from (2nd) without attribute 'never')\n\n");
3531 end if;
3532 end if;
3533 if debug then execStat("TEARINGHEURISTIC3"); end if;
3534
3535 // 4. Assess the potential tearing variables
3536 // *****************************************
3537 // 4.1 Determine for each variable the number of equations it could causalize considering impossible assignments and save them in counts1
3538 3160 (potentialTVars,counts1) := selectCausalizingVars(mtIn,potentialTVars2, me=meIn,ass1In=ass1In,selEqsSetArray=selectCausalVarsPrepareSelectionSet(causEq, arrayLength(ass1In)));
3539
3540 // If none of the variables is able to causalize an equation in the next step, use the previous selection
3541
2/2
✓ Branch 0 taken 178 times.
✓ Branch 1 taken 2982 times.
3160 if listEmpty(potentialTVars) then
3542 178 potentialTVars := potentialTVars2;
3543 178 counts1 := List.fill(0, listLength(potentialTVars2));
3544 end if;
3545 if debug then execStat("TEARINGHEURISTIC4_1"); end if;
3546
3547 // 4.2 Determine for each variable the number of impossible assignments and save them in counts2
3548 3160 (_,counts2,_) := getAllVarsWithMostImpAss(potentialTVars,ass2In,metIn);
3549 if debug then execStat("TEARINGHEURISTIC4_2"); end if;
3550
3551 // 4.3 Calculate the sum of number of impossible assignments and causalizable equations for each variable and save them in points
3552 3160 points := List.threadMap(counts1,counts2,intAdd);
3553
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3160 times.
3160 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3554 ✗ print("\n4th (Points): "+ stringDelimitList(List.map(listReverse(points),intString),",")+"\n(Sum of impossible assignments and causalizable equations)\n");
3555 end if;
3556 if debug then execStat("TEARINGHEURISTIC4_3"); end if;
3557
3558 // 4.4 Prefer variables with annotation attribute '__OpenModelica_tearingSelect = TearingSelect.prefer'
3559
2/2
✓ Branch 0 taken 1522 times.
✓ Branch 1 taken 1638 times.
3160 if not listEmpty(tSel_prefer) then
3560 1522 points := preferAvoidVariables(potentialTVars, points, tSel_prefer, 3.0);
3561
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1522 times.
1522 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3562 ✗ print(" (Points): "+ stringDelimitList(List.map(points,intString),",")+"\n(Points after preferring variables with attribute 'prefer')\n");
3563 end if;
3564 end if;
3565 if debug then execStat("TEARINGHEURISTIC4_4"); end if;
3566
3567 // 4.5 Avoid variables with annotation attribute '__OpenModelica_tearingSelect = TearingSelect.avoid'
3568
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3155 times.
3160 if not listEmpty(tSel_avoid) then
3569 5 points := preferAvoidVariables(potentialTVars, points, tSel_avoid, 0.334);
3570
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3571 ✗ print(" (Points): "+ stringDelimitList(List.map(points,intString),",")+"\n(Points after discrimination against variables with attribute 'avoid')\n");
3572 end if;
3573 end if;
3574 if debug then execStat("TEARINGHEURISTIC4_5"); end if;
3575
3576 // 5. Choose vars with most points and save them in bestPotentialTVars
3577 // *******************************************************************
3578 3160 (bestPotentialTVars, maxPoints) := getAllVarsWithMostPoints(potentialTVars, points);
3579
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3160 times.
3160 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3580 ✗ print("\n5th: "+ stringDelimitList(List.map(bestPotentialTVars,intString),",")+"\n(Variables from (3rd) with most points [" + intString(maxPoints) + "])\n\n");
3581 end if;
3582 if debug then execStat("TEARINGHEURISTIC5"); end if;
3583
3584 // 6. Choose one var with most occurrence in equations as potentials (tearing variable)
3585 // ************************************************************************************
3586 3160 (edges,potentials) := getVarOccurringInMostEquations(mtIn,bestPotentialTVars);
3587
1/2
✓ Branch 1 taken 3160 times.
✗ Branch 2 not taken.
3160 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3588 ✗ print("6th: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Chosen tearing variable. One from (5th) with most occurrence in equations (" + intString(edges) +" times))\n\n");
3589 end if;
3590 if debug then execStat("TEARINGHEURISTIC6"); end if;
3591 end ModifiedCellierHeuristic_3;
3592
3593
3594 protected function ModifiedCellierHeuristic_4 " Heuristic to find a preferably good tearing variable [MC4].
3595 author: ptaeuber FHB 2013-2015"
3596 extends TearingHeuristic;
3597 protected
3598 Integer edges;
3599 list<Integer> potentials1,potentials2,potentials3,potentials4,potentials5,potentials6,potentials7,potentials8,potentials9,potentials10,selectedvars,count;
3600 algorithm
3601 // Cellier heuristic [MC4]
3602
3603 // 1. Use heuristics MC1, MC2, MC11, MC21, MC12, MC22, MC13, MC23, MC231, MC3 to determine their potential sets
3604 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3605 ✗ print("Heuristic uses all modified Cellier-Heuristics\n\nHeuristic [MC1]\n"+ BORDER +"\n");
3606 end if;
3607 ✗ potentials1 := ModifiedCellierHeuristic_1(mIn,mtIn,meIn,metIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3608 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3609 ✗ print("\n\nHeuristic [MC2]\n"+ BORDER +"\n");
3610 end if;
3611 ✗ potentials2 := ModifiedCellierHeuristic_2(mIn,mtIn,meIn,metIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3612 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3613 ✗ print("\n\nHeuristic [MC11]\n"+ BORDER +"\n");
3614 end if;
3615 ✗ potentials3 := ModifiedCellierHeuristic_1_1(mIn,mtIn,meIn,metIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3616 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3617 ✗ print("\n\nHeuristic [MC21]\n"+ BORDER +"\n");
3618 end if;
3619 ✗ potentials4 := ModifiedCellierHeuristic_2_1(mIn,mtIn,meIn,metIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3620 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3621 ✗ print("\n\nHeuristic [MC12]\n"+ BORDER +"\n");
3622 end if;
3623 ✗ potentials5 := ModifiedCellierHeuristic_1_2(mIn,mtIn,meIn,metIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3624 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3625 ✗ print("\n\nHeuristic [MC22]\n"+ BORDER +"\n");
3626 end if;
3627 ✗ potentials6 := ModifiedCellierHeuristic_2_2(mIn,mtIn,meIn,metIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3628 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3629 ✗ print("\n\nHeuristic [MC13]\n"+ BORDER +"\n");
3630 end if;
3631 ✗ potentials7 := ModifiedCellierHeuristic_1_3(mIn,mtIn,meIn,metIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3632 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3633 ✗ print("\n\nHeuristic [MC23]\n"+ BORDER +"\n");
3634 end if;
3635 ✗ potentials8 := ModifiedCellierHeuristic_2_3(mIn,mtIn,meIn,metIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3636 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3637 ✗ print("\n\nHeuristic [MC231]\n"+ BORDER +"\n");
3638 end if;
3639 ✗ potentials9 := ModifiedCellierHeuristic_2_3_1(mIn,mtIn,meIn,metIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3640 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3641 ✗ print("\n\nHeuristic [MC3]\n"+ BORDER +"\n");
3642 end if;
3643 ✗ potentials10 := ModifiedCellierHeuristic_3(mIn,mtIn,meIn,metIn,ass1In,ass2In,discreteVars,tSel_prefer,tSel_avoid,tSel_never,mapEqnIncRow,mapIncRowEqn);
3644 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3645 ✗ print(BORDER + "\n\nSynopsis:\n=========\n[MC1]: " + stringDelimitList(List.map(potentials1,intString),",")+"\n");
3646 ✗ print("[MC2]: " + stringDelimitList(List.map(potentials2,intString),",")+"\n");
3647 ✗ print("[MC11]: " + stringDelimitList(List.map(potentials3,intString),",")+"\n");
3648 ✗ print("[MC21]: " + stringDelimitList(List.map(potentials4,intString),",")+"\n");
3649 ✗ print("[MC12]: " + stringDelimitList(List.map(potentials5,intString),",")+"\n");
3650 ✗ print("[MC22]: " + stringDelimitList(List.map(potentials6,intString),",")+"\n");
3651 ✗ print("[MC13]: " + stringDelimitList(List.map(potentials7,intString),",")+"\n");
3652 ✗ print("[MC23]: " + stringDelimitList(List.map(potentials8,intString),",")+"\n");
3653 ✗ print("[MC231]: " + stringDelimitList(List.map(potentials9,intString),",")+"\n");
3654 ✗ print("[MC3]: " + stringDelimitList(List.map(potentials10,intString),",")+"\n\n");
3655 end if;
3656
3657 // 2. Collect all variables from different potential-sets in one list
3658 ✗ selectedvars := listAppend(potentials1,listAppend(potentials2,listAppend(potentials3,listAppend(potentials4,listAppend(potentials5,listAppend(potentials6,listAppend(potentials7,listAppend(potentials8,listAppend(potentials9,potentials10)))))))));
3659 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3660 ✗ print("1st: "+ stringDelimitList(List.map(selectedvars,intString),",")+"\n(All potentials)\n\n");
3661 end if;
3662
3663 // 3. determine potentials with most occurrence in potential sets
3664 ✗ (count,selectedvars,_) := countMultiples(arrayCreate(1,selectedvars));
3665 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3666 ✗ print("2nd: "+ stringDelimitList(List.map(selectedvars,intString),",")+"\n(Variables from (1st) occurring in most potential-sets (" + stringDelimitList(List.map(count,intString),",") + " sets))\n\n");
3667 end if;
3668
3669 // 4. Choose vars with most occurrence in equations as potentials
3670 ✗ (edges,potentials) := getVarOccurringInMostEquations(mtIn, selectedvars);
3671
3672 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3673 ✗ print("3rd: "+ stringDelimitList(List.map(potentials,intString),",")+"\n(Chosen tearing variable. One from from (2nd) with most occurrence in equations (" + intString(edges) +" times))\n\n\n");
3674 end if;
3675 end ModifiedCellierHeuristic_4;
3676
3677
3678 protected function preferAvoidVariables
3679 "multiplies points of variables with annotation attribute '__OpenModelica_tearingSelect in [prefer, avoid]' with factor
3680 author: ptaeuber FHB 2014-05"
3681 input list<Integer> varsIn;
3682 input output list<Integer> points;
3683 input list<Integer> preferAvoidIn;
3684 input Real factor;
3685 protected
3686 array<Integer> pointsArr, firstPos "1-based position of each variable in varsIn";
3687 Integer pos, maxVar;
3688 algorithm
3689
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 1537 times.
1615 if listEmpty(preferAvoidIn) then
3690 78 return;
3691 end if;
3692 1537 maxVar := List.fold(varsIn, intMax, 0);
3693 1537 firstPos := arrayCreate(maxVar, 0);
3694 pos := 1;
3695
2/2
✓ Branch 0 taken 126502 times.
✓ Branch 1 taken 1537 times.
128039 for v in varsIn loop
3696
2/4
✓ Branch 0 taken 126502 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 126502 times.
✗ Branch 4 not taken.
126502 if v > 0 and firstPos[v] == 0 then
3697 126502 firstPos[v] := pos;
3698 end if;
3699 126502 pos := pos + 1;
3700 end for;
3701 1537 pointsArr := listArray(points);
3702
2/2
✓ Branch 0 taken 128852 times.
✓ Branch 1 taken 1537 times.
130389 for preferAvoidVar in preferAvoidIn loop
3703
3/4
✓ Branch 0 taken 128852 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 121586 times.
✓ Branch 3 taken 7266 times.
128852 if preferAvoidVar > 0 and preferAvoidVar <= maxVar then
3704 121586 pos := firstPos[preferAvoidVar];
3705 else
3706 try
3707 7266 pos := List.position(preferAvoidVar, varsIn);
3708 else
3709 pos := 0;
3710 end try;
3711 end if;
3712
3/4
✓ Branch 0 taken 41347 times.
✓ Branch 1 taken 87505 times.
✓ Branch 2 taken 41347 times.
✗ Branch 3 not taken.
170199 if pos > 0 and pos <= arrayLength(pointsArr) then
3713 41347 pointsArr[pos] := realInt(realMul(factor, intReal(pointsArr[pos])));
3714 end if;
3715 end for;
3716 1537 points := arrayList(pointsArr);
3717 end preferAvoidVariables;
3718
3719
3720 protected function selectCausalVarsPrepareSelectionSet
3721 "selectMostCausalizingVars takes as input an array ass1In. selEqs and each row
3722 has indexes into ass1In and we need to intersect selEqs with each row.
3723 This prepares a set by making an array of all possible indexes and
3724 marking the ones that exist in selEqs."
3725 input list<Integer> selEqs;
3726 input Integer ass1In_size;
3727 output array<Boolean> selEqsSetArray;
3728 algorithm
3729 3312 selEqsSetArray := arrayCreate(ass1In_size, false);
3730
2/2
✓ Branch 1 taken 136302 times.
✓ Branch 2 taken 3312 times.
139614 for e in selEqs loop
3731 136302 arrayUpdate(selEqsSetArray, e, true);
3732 end for;
3733 end selectCausalVarsPrepareSelectionSet;
3734
3735
3736 protected function selectMostCausalizingVars
3737 " determines the variables causalizing the most equations in the next step.
3738 author: ptaeuber FHB 2013-2015"
3739 input BackendDAE.AdjacencyMatrix inMt;
3740 input list<Integer> selVars;
3741 input BackendDAE.AdjacencyMatrixEnhanced me;
3742 input array<Integer> ass1In;
3743 input array<Boolean> selEqsSetArray;
3744 output list<Integer> cVars = {};
3745 output list<Integer> counts = {};
3746 protected
3747 list<Integer> row;
3748 Integer size,num = 0;
3749 algorithm
3750
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
24 for var in selVars loop
3751 20 row := arrayGet(inMt, var);
3752 20 arrayUpdate(ass1In,var,1);
3753 size := 0;
3754
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 20 times.
80 for i in row loop
3755
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 32 times.
60 if arrayGet(selEqsSetArray,i) then
3756 28 size := sizeOfAssignable(i,me,ass1In,size);
3757 end if;
3758 end for;
3759 20 arrayUpdate(ass1In,var,-1);
3760
3761
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 12 times.
20 if size < num then
3762 counts := size::counts;
3763 elseif size == num then
3764 cVars := var::cVars;
3765 counts := size::counts;
3766 else
3767 cVars := {var};
3768 num := size;
3769 counts := size::counts;
3770 end if;
3771
3772
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
20 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3773 ✗ print("Var " + intString(var) + " would causalize " + intString(size) + " Eqns\n");
3774 end if;
3775 end for;
3776 end selectMostCausalizingVars;
3777
3778
3779 protected function selectCausalizingVars
3780 " returns the variables causalizing at least one equation in the next step and the counts in a second list.
3781 author: ptaeuber"
3782 input BackendDAE.AdjacencyMatrix inMt;
3783 input list<Integer> selVars;
3784 input BackendDAE.AdjacencyMatrixEnhanced me;
3785 input array<Integer> ass1In;
3786 input array<Boolean> selEqsSetArray;
3787 output list<Integer> cVars = {};
3788 output list<Integer> counts = {};
3789 protected
3790 list<Integer> row;
3791 Integer size;
3792 algorithm
3793
2/2
✓ Branch 0 taken 335383 times.
✓ Branch 1 taken 3160 times.
338543 for var in selVars loop
3794 335383 row := arrayGet(inMt, var);
3795 335383 arrayUpdate(ass1In,var,1);
3796 size := 0;
3797
2/2
✓ Branch 0 taken 1194532 times.
✓ Branch 1 taken 335383 times.
1529915 for i in row loop
3798
2/2
✓ Branch 1 taken 240603 times.
✓ Branch 2 taken 953929 times.
1194532 if arrayGet(selEqsSetArray,i) then
3799 240603 size := sizeOfAssignable(i,me,ass1In,size);
3800 end if;
3801 end for;
3802 335383 arrayUpdate(ass1In,var,-1);
3803
3804
2/2
✓ Branch 0 taken 142222 times.
✓ Branch 1 taken 193161 times.
335383 if not size == 0 then
3805 cVars := var::cVars;
3806 counts := size::counts;
3807 end if;
3808
3809
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 335383 times.
335383 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3810 ✗ print("Var " + intString(var) + " would causalize " + intString(size) + " Eqns\n");
3811 end if;
3812 end for;
3813 end selectCausalizingVars;
3814
3815
3816 protected function selectOneMostCausalizingVar
3817 " determines one variable causalizing the most equations in the next step.
3818 author: ptaeuber"
3819 input BackendDAE.AdjacencyMatrix inMt;
3820 input list<Integer> selVars;
3821 input BackendDAE.AdjacencyMatrixEnhanced me;
3822 input array<Integer> ass1In;
3823 input array<Boolean> selEqsSetArray;
3824 output list<Integer> cVars = {};
3825 output Integer outMax = 0;
3826 protected
3827 list<Integer> row;
3828 Integer size;
3829 algorithm
3830
2/2
✓ Branch 0 taken 3351 times.
✓ Branch 1 taken 148 times.
3499 for var in selVars loop
3831 3351 row := arrayGet(inMt, var);
3832 3351 arrayUpdate(ass1In,var,1);
3833 size := 0;
3834
2/2
✓ Branch 0 taken 29643 times.
✓ Branch 1 taken 3351 times.
32994 for i in row loop
3835
2/2
✓ Branch 1 taken 162 times.
✓ Branch 2 taken 29481 times.
29643 if arrayGet(selEqsSetArray,i) then
3836 162 size := sizeOfAssignable(i,me,ass1In,size);
3837 end if;
3838 end for;
3839 3351 arrayUpdate(ass1In,var,-1);
3840
3841
2/2
✓ Branch 0 taken 3029 times.
✓ Branch 1 taken 322 times.
3351 if intGe(size,outMax) then
3842 cVars := {var};
3843 outMax := size;
3844 end if;
3845
3846
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3351 times.
3351 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3847 ✗ print("Var " + intString(var) + " would causalize " + intString(size) + " Eqns\n");
3848 end if;
3849 end for;
3850 end selectOneMostCausalizingVar;
3851
3852
3853 protected function getOneVarWithMostPoints
3854 "function to find a variable from inVarList with most points in corresponding inPointsLst.
3855 author: ptaeuber"
3856 input list<Integer> inVarList;
3857 input list<Integer> inPointsLst;
3858 output list<Integer> outVarList={};
3859 output Integer outMax;
3860 protected
3861 Integer index=1;
3862 algorithm
3863
4/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 2 times.
12 outMax := max(i for i in inPointsLst);
3864
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 for i in inPointsLst loop
3865
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if i==outMax then
3866 2 outVarList := {listGet(inVarList,index)};
3867 2 return;
3868 end if;
3869 2 index := index+1;
3870 end for;
3871 end getOneVarWithMostPoints;
3872
3873
3874 protected function getAllVarsWithMostPoints
3875 "function to find all variables from inVarList with most points in corresponding inPointsLst.
3876 author: ptaeuber"
3877 input list<Integer> inVarList;
3878 input list<Integer> inPointsLst;
3879 input output list<Integer> outVarList={};
3880 input output Integer outMax=-1;
3881 algorithm
3882 () := match(inVarList, inPointsLst)
3883 local
3884 Integer p,v;
3885 list<Integer> prest,vrest;
3886 case(v::{}, p::{})
3887 algorithm
3888
2/2
✓ Branch 0 taken 345 times.
✓ Branch 1 taken 2815 times.
3160 if intGt(p, outMax) then
3889 345 outMax := p;
3890 outVarList := {v};
3891 elseif intEq(p, outMax) then
3892 outVarList := v::outVarList;
3893 end if;
3894 then ();
3895 case(v::vrest, p::prest)
3896 algorithm
3897
2/2
✓ Branch 0 taken 6588 times.
✓ Branch 1 taken 133821 times.
140409 if intGt(p, outMax) then
3898 6588 outMax := p;
3899 outVarList := {v};
3900 elseif intEq(p, outMax) then
3901 outVarList := v::outVarList;
3902 end if;
3903 140409 (outVarList, outMax) := getAllVarsWithMostPoints(vrest, prest, outVarList, outMax);
3904 then ();
3905 else
3906 algorithm
3907 ✗ Error.addCompilerError("Tearing.getAllVarsWithMostPoints: Finding variables with most points failed.");
3908 ✗ fail();
3909 then ();
3910 end match;
3911 end getAllVarsWithMostPoints;
3912
3913
3914 protected function sizeOfAssignable
3915 " calculates the number of equations a potential tvar would
3916 causalize considering the impossible assignments
3917 author: ptaeuber FHB 2013-10"
3918 input Integer Eqn;
3919 input BackendDAE.AdjacencyMatrixEnhanced me;
3920 input array<Integer> ass1;
3921 input Integer inSize;
3922 output Integer outSize;
3923 protected
3924 BackendDAE.AdjacencyMatrixElementEnhanced vars;
3925 Boolean b;
3926 algorithm
3927 240793 vars := List.removeOnTrue(ass1,isAssignedSaveEnhanced,me[Eqn]);
3928 240793 b := solvableLst(vars);
3929
2/2
✓ Branch 0 taken 183722 times.
✓ Branch 1 taken 57071 times.
240793 outSize := if b then inSize+1 else inSize;
3930 end sizeOfAssignable;
3931
3932
3933 protected function getAllVarsWithMostImpAss
3934 " function to return the variables with the highest number of impossible assignments
3935 considering the current matching
3936 author: ptaeuber FHB 2013-10"
3937 input list<Integer> inPotentials;
3938 input array<Integer> ass2;
3939 input BackendDAE.AdjacencyMatrixEnhanced meT;
3940 output list<Integer> outPotentials={};
3941 output list<Integer> outCounts={};
3942 output Integer outMax=0;
3943 protected
3944 Integer count;
3945 BackendDAE.AdjacencyMatrixElementEnhanced elem;
3946 algorithm
3947
2/2
✓ Branch 0 taken 143589 times.
✓ Branch 1 taken 3164 times.
146753 for v in inPotentials loop
3948 143589 elem := List.removeOnTrue(ass2,isAssignedSaveEnhanced,meT[v]);
3949 143589 count := countImpossibleAss(elem);
3950
3951
2/2
✓ Branch 0 taken 4572 times.
✓ Branch 1 taken 139017 times.
143589 if count > outMax then
3952 outPotentials := {v};
3953 outMax := count;
3954 elseif count == outMax then
3955 outPotentials := v::outPotentials;
3956 end if;
3957 outCounts := count::outCounts;
3958
3959
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 143589 times.
143589 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3960 ✗ print("Var " + intString(v) + " has " + intString(count) + " incident impossible assignments\n");
3961 end if;
3962 end for;
3963 3164 outCounts := listReverse(outCounts);
3964 end getAllVarsWithMostImpAss;
3965
3966
3967 protected function getOneVarWithMostImpAss
3968 " function to return one variable with the highest number of impossible assignments
3969 considering the current matching
3970 author: ptaeuber"
3971 input list<Integer> inPotentials;
3972 input array<Integer> ass2;
3973 input BackendDAE.AdjacencyMatrixEnhanced meT;
3974 output list<Integer> outPotentials={};
3975 output Integer outMax=-1;
3976 protected
3977 Integer count;
3978 BackendDAE.AdjacencyMatrixElementEnhanced elem;
3979 algorithm
3980
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for v in inPotentials loop
3981 4 elem := List.removeOnTrue(ass2,isAssignedSaveEnhanced,meT[v]);
3982 4 count := countImpossibleAss(elem);
3983
3984
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if count > outMax then
3985 outPotentials := {v};
3986 outMax := count;
3987 end if;
3988
3989
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
3990 ✗ print("Var " + intString(v) + " has " + intString(count) + " incident impossible assignments\n");
3991 end if;
3992 end for;
3993 end getOneVarWithMostImpAss;
3994
3995
3996 protected function countImpossibleAss
3997 " helper function for getAllVarsWithMostImpAss,
3998 traverses AdjacencyMatrixElementEnhanced and counts the number of impossible assignments of one var
3999 author: ptaeuber FHB 2013-10"
4000 input BackendDAE.AdjacencyMatrixElementEnhanced elem;
4001 output Integer outCount = 0;
4002 protected
4003 BackendDAE.Solvability s;
4004 algorithm
4005
2/2
✓ Branch 0 taken 554478 times.
✓ Branch 1 taken 143593 times.
698071 for e in elem loop
4006 554478 (_,s,_) := e;
4007
2/2
✓ Branch 1 taken 276742 times.
✓ Branch 2 taken 277736 times.
554478 if not solvable(s) then
4008 276742 outCount := outCount + 1;
4009 end if;
4010 end for;
4011 end countImpossibleAss;
4012
4013
4014 protected function TarjanMatching "Modified matching algorithm according to Tarjan as it is used by Cellier.
4015 author: ptaeuber 2013-2015"
4016 input BackendDAE.AdjacencyMatrix mIn;
4017 input BackendDAE.AdjacencyMatrixT mtIn;
4018 input BackendDAE.AdjacencyMatrixEnhanced meIn;
4019 input BackendDAE.AdjacencyMatrixTEnhanced meTIn;
4020 input array<Integer> ass1In,ass2In;
4021 input list<Integer> orderIn;
4022 input array<list<Integer>> mapEqnIncRow;
4023 input array<Integer> mapIncRowEqn;
4024 input array<Integer> eqnNonlinPoints;
4025 input Boolean forceDegree1 "ticket #15433: enable variable-driven degree-1 forced assignment (set by CellierTearing only for initialization components that have a $START.x->x cycle)";
4026 output list<Integer> orderOut;
4027 output Boolean causal;
4028 protected
4029 list<Integer> unassigned;
4030 list<Integer> order=orderIn;
4031 Boolean assignable = true;
4032 constant Boolean debug = false;
4033 algorithm
4034
2/2
✓ Branch 0 taken 32723 times.
✓ Branch 1 taken 4103 times.
36826 while assignable loop
4035
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 32723 times.
32723 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
4036 ✗ print("\nTarjanAssignment:\n");
4037 end if;
4038 32723 (order,assignable) := TarjanAssignment(mIn,mtIn,meIn,meTIn,ass1In,ass2In,order,mapEqnIncRow,mapIncRowEqn,eqnNonlinPoints,forceDegree1);
4039 end while;
4040 if debug then execStat("Tearing.TarjanMatching iters done"); end if;
4041
4042 4103 unassigned := getUnassigned(ass1In);
4043
2/2
✓ Branch 0 taken 1957 times.
✓ Branch 1 taken 2146 times.
4103 if listEmpty(unassigned) then
4044
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1957 times.
1957 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
4045 ✗ print("\ncausal\n");
4046 end if;
4047 1957 orderOut := listReverse(order);
4048 causal := true;
4049 else
4050
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2146 times.
2146 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
4051 ✗ print("\nnoncausal\n");
4052 end if;
4053 orderOut := order;
4054 causal := false;
4055 end if;
4056 if debug then execStat("Tearing.TarjanMatching done"); end if;
4057 end TarjanMatching;
4058
4059
4060 protected function TarjanAssignment " finds assignable equations and variables and assigns
4061 author: ptaeuber FHB 2013-2015"
4062 input BackendDAE.AdjacencyMatrix mIn;
4063 input BackendDAE.AdjacencyMatrixT mtIn;
4064 input BackendDAE.AdjacencyMatrixEnhanced meIn;
4065 input BackendDAE.AdjacencyMatrixTEnhanced meTIn;
4066 input array<Integer> ass1In,ass2In;
4067 input list<Integer> orderIn;
4068 input array<list<Integer>> mapEqnIncRow;
4069 input array<Integer> mapIncRowEqn;
4070 input array<Integer> eqnNonlinPoints;
4071 input Boolean forceDegree1 "ticket #15433: enable variable-driven degree-1 forced assignment (set by CellierTearing only for initialization components that have a $START.x->x cycle)";
4072 output list<Integer> orderOut = orderIn;
4073 output Boolean assignable = false;
4074 protected
4075 Integer eq_coll;
4076 list<Integer> assEq_coll, eqns = {}, vars = {};
4077 algorithm
4078 // ticket #15433: variable-driven forced assignment of degree-1 variables. The defect this repairs
4079 // is a $START.x variable solvable in exactly one still-unassigned equation (its defining initial
4080 // equation) but structurally incident to more than one (it is linked to the state's equations); it
4081 // must be matched to its defining equation, otherwise the equation-driven pass below (which matches
4082 // on structural incidence) lets a higher-degree variable steal that equation, orphaning the
4083 // $START.x variable and producing a structurally singular torn system. forceDegree1 already gates
4084 // this to exactly the initialization components that exhibit the $START.x->x cycle (see
4085 // CellierTearing), so it runs only where the defect can occur.
4086
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32723 times.
32723 if forceDegree1 then
4087 try
4088 ✗ (eq_coll,eqns,vars) := getNextDegree1Var(mtIn,meTIn,ass1In,ass2In,mapEqnIncRow,mapIncRowEqn);
4089 orderOut := eq_coll::orderOut;
4090 assignable := true;
4091 else
4092 end try;
4093 end if;
4094
4095
1/2
✓ Branch 0 taken 32723 times.
✗ Branch 1 not taken.
32723 if not assignable then
4096 // find equations with one variable
4097 32723 assEq_coll := traverseCollectiveEqnsforAssignable(ass2In,mIn,mapEqnIncRow);
4098
4099
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 32723 times.
32723 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
4100 ✗ print("New assEq_coll: "+stringDelimitList(List.map(assEq_coll,intString),",")+"\n");
4101 end if;
4102
4103 // NOTE: For tearing of strong components with the same number of equations and variables and with a late choice of the
4104 // residual equation it is not possible to match starting from the variables, so this case is not considered.
4105 // For other tearing structures this case has to be added.
4106
4107 // Get the next solvable equation from the equation queue
4108 try
4109 32723 (eq_coll,eqns,vars) := getNextSolvableEqn(assEq_coll,mIn,meIn,ass1In,ass2In,mapEqnIncRow,mapIncRowEqn,eqnNonlinPoints);
4110 orderOut := eq_coll::orderOut;
4111 assignable := true;
4112 else
4113 end try;
4114 end if;
4115
4116 // Make the assignment if possible
4117
2/2
✓ Branch 0 taken 28620 times.
✓ Branch 1 taken 4103 times.
32723 if assignable then
4118 28620 makeAssignment(eqns,vars,ass1In,ass2In,mIn,mtIn);
4119 end if;
4120
4121
1/2
✓ Branch 1 taken 32723 times.
✗ Branch 2 not taken.
32723 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
4122 ✗ print("order: "+stringDelimitList(List.map(listReverse(orderOut),intString),",")+"\n\n");
4123 end if;
4124 end TarjanAssignment;
4125
4126
4127 protected function getNextDegree1Var
4128 "ticket #15433: finds an unassigned variable that is SOLVABLE in exactly one still-unassigned
4129 scalar equation while being STRUCTURALLY incident to more than one, and returns the assignment
4130 for it. This is exactly the case the equation-driven pass mishandles: it matches on the plain
4131 structural adjacency mtIn, where such a variable looks high-degree (during initialization a
4132 $START.x variable is structurally linked to the state's equations), so a higher-degree variable
4133 can steal its only solvable equation and orphan it, producing a structurally singular torn
4134 system. The solvability is read from the enhanced transposed adjacency meTIn (entries
4135 (eqn, solvability, _); unsolvable entries are ignored via BackendDAEUtil.isSolvable). The caller
4136 (TarjanAssignment) only invokes this when forceDegree1 is set, which CellierTearing restricts to
4137 initialization components that have a $START.x->x cycle (a variable whose start attribute
4138 transitively depends on the variable itself) - the only place this matching defect occurs. The
4139 search is restricted to variables whose structural degree exceeds their solvable degree, so an
4140 ordinary degree-1 variable handled correctly by the equation-driven pass is never reordered here.
4141 Only scalar equations are forced, to stay on the safe side of array/collective equations. Fails if
4142 there is no such variable."
4143 input BackendDAE.AdjacencyMatrixT mtIn;
4144 input BackendDAE.AdjacencyMatrixTEnhanced meTIn;
4145 input array<Integer> ass1;
4146 input array<Integer> ass2;
4147 input array<list<Integer>> mapEqnIncRow;
4148 input array<Integer> mapIncRowEqn;
4149 output Integer eqCollOut;
4150 output list<Integer> eqnsOut;
4151 output list<Integer> varsOut;
4152 protected
4153 Integer e, eqColl, cnt, theEqn;
4154 BackendDAE.Solvability s;
4155 algorithm
4156 ✗ for v in 1:arrayLength(meTIn) loop
4157 // only consider unassigned variables that the structural pass sees as high-degree (degree > 1);
4158 // a variable structurally incident to a single remaining equation is matched there correctly by
4159 // the equation-driven pass and must not be reordered here. ticket #15433: only a structurally-
4160 // divergent var (struct degree > 1 but solvable degree == 1 below) - the matching defect the
4161 // equation-driven pass mishandles - is forced.
4162 ✗ if arrayGet(ass1, v) == -1 and listLength(arrayGet(mtIn, v)) > 1 then
4163 // count the still-unassigned equations this variable can actually be solved in
4164 cnt := 0;
4165 theEqn := -1;
4166 ✗ for entry in meTIn[v] loop
4167 ✗ (e, s, _) := entry;
4168 ✗ if arrayGet(ass2, e) == -1 and BackendDAEUtil.isSolvable(s) then
4169 ✗ cnt := cnt + 1;
4170 theEqn := e;
4171 ✗ if cnt > 1 then break; end if;
4172 end if;
4173 end for;
4174 ✗ if cnt == 1 then
4175 ✗ eqColl := mapIncRowEqn[theEqn];
4176 // only force scalar equations; the degree-1 variable v must claim theEqn before a
4177 // higher-degree variable does.
4178 ✗ if listLength(mapEqnIncRow[eqColl]) == 1 then
4179 eqCollOut := eqColl;
4180 eqnsOut := {theEqn};
4181 varsOut := {v};
4182 ✗ return;
4183 end if;
4184 end if;
4185 end if;
4186 end for;
4187 ✗ fail();
4188 end getNextDegree1Var;
4189
4190 protected function tornMatchingIsStructurallySingular
4191 "ticket #15433: returns true when the plain Cellier matching left a variable that is solvable in
4192 EXACTLY ONE equation (per the enhanced solvability adjacency) matched to some OTHER variable, so
4193 that degree-1 variable is orphaned and can be solved nowhere - a structural singularity. This is
4194 the cable's $START.x defect: its single defining equation $START.x = sp is stolen by the high-
4195 degree r_start, leaving $START.x to be paired with a pin equation it does not occur in. The
4196 degree-1 forcing repairs exactly this (it gives every degree-1-solvable variable its unique
4197 equation), so this test is symmetric with the forcing: it fires precisely when forcing would
4198 change the matching. A healthy matching - e.g. the OpenIPSL PSSE controllers, which initialize
4199 correctly without forcing - gives every degree-1-solvable variable its unique solvable equation
4200 and returns false, so those components are left on the plain (master) result.
4201 ass2[e] is the variable matched to scalar equation e - the SAME index space as the enhanced
4202 matrix equation indices and as getNextDegree1Var's ass2 use. The value of ass2[e] discriminates
4203 the two start-cycle classes: a POSITIVE variable index means equation e is solved for THAT other
4204 variable, so a degree-1 variable whose only solvable equation is taken this way is genuinely
4205 orphaned (the cable's $START.x defect -> singular); a value of -2 means e became a RESIDUAL
4206 (getNextSolvableEqn marks unsolvable/residual rows with -2), in which case the degree-1 variable is
4207 simply the iteration variable of a residual it actually occurs in - the normal, non-singular
4208 nonlinear tearing of the OpenIPSL PSSE controllers, which initialize correctly without forcing.
4209 meTFull is the UNMUTATED enhanced transposed adjacency (variable -> list of (equation, solvability,
4210 _))."
4211 input array<Integer> ass2;
4212 input BackendDAE.AdjacencyMatrixTEnhanced meTFull;
4213 input Integer size;
4214 input BackendDAE.Variables vars;
4215 output Boolean singular = false;
4216 protected
4217 Integer ev, cnt, theEqn, owner;
4218 BackendDAE.Solvability s;
4219 DAE.ComponentRef cref;
4220 algorithm
4221 ✗ for v in 1:size loop
4222 ✗ cref := BackendVariable.varCref(BackendVariable.getVarAt(vars, v));
4223 // restrict to the start-value-cycle singularity: only a $START.x variable orphaned this way is
4224 // the cable defect. A non-$START value variable whose unique solvable equation is taken by
4225 // another variable (e.g. AC8B's rotatingExciterWithDemagnetizationVarLim.feedback.y) instead
4226 // becomes the iteration variable of a residual it DOES occur in - non-singular, and forcing it
4227 // only reorders a healthy system and breaks initialization.
4228 ✗ if ComponentReference.isStartCref(cref) then
4229 // count the equations $START.x can actually be solved in (enhanced solvability), recording the
4230 // unique one when there is exactly one
4231 cnt := 0;
4232 theEqn := -1;
4233 ✗ for entry in arrayGet(meTFull, v) loop
4234 ✗ (ev, s, _) := entry;
4235 ✗ if BackendDAEUtil.isSolvable(s) then
4236 ✗ cnt := cnt + 1;
4237 theEqn := ev;
4238 ✗ if cnt > 1 then break; end if;
4239 end if;
4240 end for;
4241 // $START.x is solvable in exactly ONE equation (its defining equation $START.x = sp). The
4242 // variable that equation got matched to (ass2[theEqn]) decides whether the matching is sound:
4243 // owner == v -> $START.x kept its own defining equation: correctly matched, healthy
4244 // (the OpenIPSL PSSE controllers - they initialize without forcing).
4245 // owner == -2 -> that equation became a residual and $START.x iterates it (it occurs there
4246 // solvably): non-singular, healthy.
4247 // owner > 0, <> v -> a DIFFERENT real variable (e.g. the cable's high-degree r_start) stole
4248 // the only equation $START.x could be solved in: $START.x is orphaned.
4249 // owner == -1 -> the equation is left unmatched and $START.x is orphaned (the inlined-
4250 // parameter case of the minimal reproducer InitStartTearingDegree1).
4251 // In the two orphaned cases $START.x can be solved nowhere and ends up paired with a residual it
4252 // does not occur in -> zero Jacobian column -> structurally singular. The degree-1 forcing
4253 // repairs exactly this by giving $START.x its defining equation back.
4254 ✗ if cnt == 1 then
4255 ✗ owner := arrayGet(ass2, theEqn);
4256 ✗ if owner <> v and owner <> -2 then
4257 // $START.x is orphaned -> the matching is structurally singular; one such variable is
4258 // enough to require the degree-1 forcing for this component
4259 singular := true;
4260 ✗ return;
4261 end if;
4262 end if;
4263 end if;
4264 end for;
4265 end tornMatchingIsStructurallySingular;
4266
4267
4268 protected function traverseSingleEqnsforAssignable
4269 " selects next equations that can be causalized without consideration of solvability
4270 author: ptaeuber FHB 2013-10"
4271 input array<Integer> inAss;
4272 input BackendDAE.AdjacencyMatrix m;
4273 input array<list<Integer>> mapEqnIncRow;
4274 input array<Integer> mapIncRowEqn;
4275 output list<Integer> selectedrows;
4276 protected
4277 Integer eqnColl,eqnSize;
4278 DoubleEnded.MutableList<Integer> delst;
4279 algorithm
4280 3314 delst := DoubleEnded.empty(0);
4281
1/2
✓ Branch 0 taken 3314 times.
✗ Branch 1 not taken.
731486 for e in 1:arrayLength(inAss) loop
4282
2/2
✓ Branch 1 taken 270597 times.
✓ Branch 2 taken 457575 times.
728172 if arrayGet(inAss,e)<>-1 then
4283 270597 continue;
4284 end if;
4285 457575 eqnColl := mapIncRowEqn[e];
4286 457575 eqnSize := listLength(mapEqnIncRow[eqnColl]);
4287
2/2
✓ Branch 2 taken 136302 times.
✓ Branch 3 taken 321273 times.
457575 if listLength(m[e]) == eqnSize + 1 then
4288
2/2
✓ Branch 0 taken 132923 times.
✓ Branch 1 taken 3379 times.
136302 if eqnSize == 1 then
4289 132923 DoubleEnded.push_back(delst, e);
4290 else
4291 3379 DoubleEnded.push_front(delst, e);
4292 end if;
4293 end if;
4294 end for;
4295 3314 selectedrows := DoubleEnded.toListAndClear(delst);
4296 end traverseSingleEqnsforAssignable;
4297
4298
4299 protected function traverseCollectiveEqnsforAssignable
4300 " selects next collective equations that can be causalized without consideration of solvability
4301 author: ptaeuber"
4302 input array<Integer> inAss;
4303 input BackendDAE.AdjacencyMatrix m;
4304 input array<list<Integer>> mapEqnIncRow;
4305 output list<Integer> selectedrows;
4306 protected
4307 Integer eqnSize,e,eqnColl=0;
4308 DoubleEnded.MutableList<Integer> delst;
4309 algorithm
4310 32784 delst := DoubleEnded.empty(0);
4311
2/2
✓ Branch 1 taken 12015884 times.
✓ Branch 2 taken 32784 times.
12048668 for eqnLst in mapEqnIncRow loop
4312 12015884 eqnColl := eqnColl + 1;
4313 12015884 e := listHead(eqnLst);
4314
2/2
✓ Branch 1 taken 5745288 times.
✓ Branch 2 taken 6270596 times.
12015884 if arrayGet(inAss,e)<>-1 then
4315 5745288 continue;
4316 end if;
4317 6270596 eqnSize := listLength(eqnLst);
4318
2/2
✓ Branch 2 taken 153288 times.
✓ Branch 3 taken 6117308 times.
6270596 if listLength(m[e]) == eqnSize then
4319
2/2
✓ Branch 0 taken 152417 times.
✓ Branch 1 taken 871 times.
153288 if eqnSize == 1 then
4320 152417 DoubleEnded.push_back(delst, eqnColl);
4321 else
4322 871 DoubleEnded.push_front(delst, eqnColl);
4323 end if;
4324 end if;
4325 end for;
4326 32784 selectedrows := DoubleEnded.toListAndClear(delst);
4327 end traverseCollectiveEqnsforAssignable;
4328
4329
4330 protected function makeAssignment
4331 " function to assign equations with variables
4332 author: ptaeuber FHB 2013-10"
4333 input list<Integer> eqns,vars;
4334 input array<Integer> ass1In,ass2In;
4335 input BackendDAE.AdjacencyMatrix mIn;
4336 input BackendDAE.AdjacencyMatrixT mtIn;
4337 protected
4338 Integer eq, var;
4339 algorithm
4340
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 28675 times.
60041 for index in 1:listLength(eqns) loop
4341 31366 eq := listGet(eqns, index);
4342 31366 var := listGet(vars, index);
4343 31366 arrayUpdate(ass1In,var,eq);
4344 31366 arrayUpdate(ass2In,eq,var);
4345
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 31366 times.
31366 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
4346 ✗ print("assignment: Eq " + intString(eq) + " - Var " + intString(var) + "\n");
4347 end if;
4348 31366 Array.replaceAtWithFill(eq,{},{},mIn);
4349 31366 deleteEntriesFromAdjacencyMatrix(mIn,mtIn,{var});
4350 31366 Array.replaceAtWithFill(var,{},{},mtIn);
4351 31366 deleteEntriesFromAdjacencyMatrix(mtIn,mIn,{eq});
4352 end for;
4353 end makeAssignment;
4354
4355
4356 protected function getNextSolvableEqn " finds equation that can be matched with respect to solvability
4357 author: ptaeuber FHB 2013-08"
4358 input list<Integer> assEq_coll;
4359 input BackendDAE.AdjacencyMatrix m;
4360 input BackendDAE.AdjacencyMatrixEnhanced me;
4361 input array<Integer> ass1;
4362 input array<Integer> ass2;
4363 input array<list<Integer>> mapEqnIncRow;
4364 input array<Integer> mapIncRowEqn;
4365 input array<Integer> eqnNonlinPoints;
4366 output Integer eqOut = 0;
4367 output list<Integer> eqnsOut = {};
4368 output list<Integer> varsOut = {};
4369 protected
4370 Boolean solvable = false;
4371 list<Integer> eqns = assEq_coll;
4372 algorithm
4373
2/2
✓ Branch 0 taken 31419 times.
✓ Branch 1 taken 4103 times.
35522 while not listEmpty(eqns) loop
4374 31419 eqOut := getMostNonlinearEquation(eqnNonlinPoints, eqns, mapEqnIncRow, mapIncRowEqn);
4375 31419 (solvable, eqnsOut, varsOut) := eqnSolvableCheck(eqOut, mapEqnIncRow, ass1, m, me);
4376 31419 eqns := List.deleteMemberOnTrue(eqOut, eqns, intEq);
4377
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 31419 times.
31419 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
4378 ✗ print("Most nonlinear equation: " + intString(eqOut) + " - solvable?: " + boolString(solvable) + "\n");
4379 end if;
4380
2/2
✓ Branch 0 taken 2799 times.
✓ Branch 1 taken 28620 times.
31419 if solvable then
4381 break;
4382 else
4383
2/2
✓ Branch 2 taken 2799 times.
✓ Branch 3 taken 2843 times.
5642 for eq in mapEqnIncRow[eqOut] loop
4384 2843 arrayUpdate(ass2,eq,-2);
4385 end for;
4386 end if;
4387 end while;
4388
2/2
✓ Branch 0 taken 4103 times.
✓ Branch 1 taken 28620 times.
32723 if not solvable then fail(); end if;
4389 end getNextSolvableEqn;
4390
4391
4392 protected function eqnSolvableCheck "
4393 returns the expanded equation(s), variables and
4394 a boolean whether the equation is solvable or not
4395 author: ptaeuber FHB 2016"
4396 input Integer eqn_coll;
4397 input array<list<Integer>> mapEqnIncRow;
4398 input array<Integer> ass1;
4399 input BackendDAE.AdjacencyMatrix m;
4400 input BackendDAE.AdjacencyMatrixEnhanced me;
4401 output Boolean solvable;
4402 output list<Integer> eqns;
4403 output list<Integer> vars;
4404 protected
4405 Integer eqn;
4406 BackendDAE.AdjacencyMatrixElementEnhanced vars_enh;
4407 algorithm
4408 31493 eqns := mapEqnIncRow[eqn_coll];
4409 31493 eqn := listHead(eqns);
4410 31493 vars := arrayGet(m,eqn);
4411 31493 vars_enh := List.removeOnTrue(ass1, isAssignedSaveEnhanced,me[eqn]);
4412 31493 solvable := solvableLst(vars_enh);
4413 end eqnSolvableCheck;
4414
4415
4416 protected function assignInnerEquations " assigns innerEquations for TearingSet
4417 author: ptaeuber FHB 2013-08"
4418 input list<Integer> inEqns "order of equations with local numbering";
4419 input list<Integer> eindex "equation indexes with global numbering";
4420 input list<Integer> vindex "variable indexes with global numbering";
4421 input array<Integer> ass2;
4422 input array<list<Integer>> mapEqnIncRow;
4423 input Option<BackendDAE.AdjacencyMatrixEnhanced> meOpt;
4424 output BackendDAE.InnerEquations outInnerEquations;
4425 algorithm
4426
4/4
✓ Branch 0 taken 28655 times.
✓ Branch 1 taken 1981 times.
✓ Branch 2 taken 28655 times.
✓ Branch 3 taken 1981 times.
30636 outInnerEquations := list(
4427 match (eqn,meOpt)
4428 local
4429 Integer eq,otherEqn;
4430 list<Integer> eqns,vars,otherVars;
4431 BackendDAE.InnerEquation innerEquation;
4432 BackendDAE.Constraints constraints;
4433 BackendDAE.AdjacencyMatrixEnhanced me;
4434 case (eq,NONE())
4435 algorithm
4436 28650 vars := List.map1r(mapEqnIncRow[eq],arrayGet,ass2);
4437 28650 otherEqn := listGet(eindex,eq);
4438 28650 otherVars := selectFromList_rev(vindex,vars);
4439 28650 then BackendDAE.INNEREQUATION(eqn=otherEqn, vars=otherVars);
4440 case (eq,SOME(me))
4441 algorithm
4442 5 eqns := mapEqnIncRow[eq];
4443 5 vars := List.map1r(eqns,arrayGet,ass2);
4444 5 otherEqn := listGet(eindex,eq);
4445 5 otherVars := selectFromList_rev(vindex,vars);
4446 5 constraints := findConstraintForInnerEquation(me[listHead(eqns)],listHead(vars));
4447
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4 times.
5 if listEmpty(constraints) then
4448 1 innerEquation := BackendDAE.INNEREQUATION(eqn=otherEqn, vars=otherVars);
4449 else
4450 4 innerEquation := BackendDAE.INNEREQUATIONCONSTRAINTS(eqn=otherEqn, vars=otherVars, cons=constraints);
4451 end if;
4452 then (innerEquation);
4453 end match for eqn in inEqns);
4454 end assignInnerEquations;
4455
4456
4457 protected function findConstraintForInnerEquation
4458 input BackendDAE.AdjacencyMatrixElementEnhanced meRow;
4459 input Integer searchIndex;
4460 output BackendDAE.Constraints constraints={};
4461 protected
4462 Integer index;
4463 BackendDAE.AdjacencyMatrixElementEnhancedEntry meElem;
4464 BackendDAE.Constraints cons;
4465 algorithm
4466
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 for meElem in meRow loop
4467 10 (index,_,cons) := meElem;
4468
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if intEq(index,searchIndex) then
4469 constraints := cons;
4470 break;
4471 end if;
4472 end for;
4473 end findConstraintForInnerEquation;
4474
4475
4476 protected function markTVarsOrResiduals
4477 " marks several tVars in ass1 or residuals in ass2
4478 author: ptaeuber FHB 2013-10"
4479 input list<Integer> markList;
4480 input array<Integer> assIn;
4481 output array<Integer> assOut = assIn;
4482 protected
4483 Integer len;
4484 algorithm
4485 len := arrayLength(assIn);
4486
2/2
✓ Branch 0 taken 2032 times.
✓ Branch 1 taken 806 times.
2838 for i in markList loop
4487 2032 arrayUpdate(assOut, i, len * 2);
4488 end for;
4489 end markTVarsOrResiduals;
4490
4491 protected function countMultiples "counts multiple entries in array<list<Integer row(list)-wise.
4492 counter gives the maximum amount of same entries and value gives the corresponding entry.
4493 if only 0s appear in the row, then (0,0).
4494 author: Waurich TUD 2013-01"
4495 input array<list<Integer>> inArr;
4496 output list<Integer> counter,numbers,values;
4497 algorithm
4498 ✗ (counter,numbers,values,_) := Array.fold(inArr,countMultiples2,({},{},{},1));
4499 end countMultiples;
4500
4501
4502 protected function countMultiples2 " FoldFunc for countMultiples.if entries appear equaly often,
4503 just one is taken.
4504 author: Waurich TUD 2013-01"
4505 input list<Integer> rowIn;
4506 input tuple<list<Integer>,list<Integer>,list<Integer>,Integer> valIn;
4507 output tuple<list<Integer>,list<Integer>,list<Integer>,Integer> valOut;
4508 protected
4509 list<Integer> counter,values,row,set,num,val,positions,numbers;
4510 Integer indx,value,number,position;
4511 algorithm
4512 ✗ (counter,_,values,indx) := valIn;
4513 ✗ row := List.removeOnTrue(0,intEq,rowIn);
4514 ✗ set := List.unique(row);
4515 ✗ if listEmpty(set) then
4516 val := {0};
4517 ✗ num := {0};
4518 else
4519 ✗ (val,num) := countMultiples3(row,set,{},{});
4520 end if;
4521 ✗ positions := maxListInt(num);
4522 ✗ position := listHead(positions);
4523 ✗ number := listGet(num,position);
4524 ✗ numbers := selectFromList(val,positions);
4525 ✗ value := listGet(val,position);
4526 ✗ counter := List.set(counter,indx,number);
4527 ✗ values := List.set(values,indx,value);
4528 ✗ valOut := (counter,numbers,values,indx+1);
4529 end countMultiples2;
4530
4531
4532 protected function countMultiples3 " helper function for countMultiples2.
4533 author:Waurich TUD 2013-01"
4534 input list<Integer> lstIn;
4535 input list<Integer> set;
4536 input list<Integer> valIn;
4537 input list<Integer> numIn;
4538 output list<Integer> valOut;
4539 output list<Integer> numOut;
4540 algorithm
4541 (valOut,numOut) := match set
4542 local
4543 Integer value,number;
4544 list<Integer> val,num,rest;
4545 case value::rest
4546 algorithm
4547 ✗ number := listLength(lstIn)-listLength(List.removeOnTrue(value,intEq,lstIn));
4548 ✗ (val,num) := countMultiples3(lstIn,rest,value::valIn,number::numIn);
4549 then
4550 (val,num);
4551 else (valIn,numIn);
4552 end match;
4553 end countMultiples3;
4554
4555
4556 protected function maxListInt
4557 "function to find maximum Integers in inList and output a list with the indexes.
4558 author: Waurich TUD 2012-11"
4559 input list<Integer> inList;
4560 output list<Integer> outList={};
4561 protected
4562 Integer maxi, index=1;
4563 algorithm
4564 ✗ maxi := max(i for i in inList);
4565 ✗ for i in inList loop
4566 ✗ if i==maxi then
4567 outList := index::outList;
4568 end if;
4569 ✗ index := index+1;
4570 end for;
4571 end maxListInt;
4572
4573
4574 protected function getMostNonlinearEquation
4575 "Function to find maximum Integers in a selection of inArray and outputs the first index.
4576 author: ptaeuber"
4577 input array<Integer> inArray;
4578 input list<Integer> inList;
4579 input array<list<Integer>> mapEqnIncRow;
4580 input array<Integer> mapIncRowEqn;
4581 output Integer index=1;
4582 protected
4583 Integer maxi;
4584 algorithm
4585
4/4
✓ Branch 0 taken 161979 times.
✓ Branch 1 taken 31419 times.
✓ Branch 2 taken 161979 times.
✓ Branch 3 taken 31419 times.
193398 maxi := max(inArray[listHead(mapEqnIncRow[i])] for i in inList);
4586
4587
1/2
✓ Branch 0 taken 71039 times.
✗ Branch 1 not taken.
71039 for i in inList loop
4588 71039 index := listHead(mapEqnIncRow[i]);
4589
2/2
✓ Branch 1 taken 31419 times.
✓ Branch 2 taken 39620 times.
71039 if inArray[index] == maxi then
4590 31419 index := mapIncRowEqn[index];
4591 31419 return;
4592 end if;
4593 end for;
4594 end getMostNonlinearEquation;
4595
4596
4597 protected function selectFromList_rev" selects Ints from inList by indexes given in selList
4598 author: Waurich TUD 2012-11"
4599 input List<Integer> inList,selList;
4600 output List<Integer> outList;
4601 protected
4602 Integer len;
4603 algorithm
4604 32691 len := listLength(inList);
4605
6/8
✓ Branch 0 taken 46447 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 46447 times.
✓ Branch 4 taken 46447 times.
✓ Branch 5 taken 32691 times.
✓ Branch 6 taken 46447 times.
✓ Branch 7 taken 32691 times.
79138 outList := list(listGet(inList,num) for num guard num > 0 and num <= len in selList);
4606 end selectFromList_rev;
4607
4608
4609 protected function selectFromList " selects Ints from inList by indexes given in selList in reverse order.
4610 auhtor: Waurich TUD 2012-11"
4611 input List<Integer> inList,selList;
4612 output List<Integer> outList = {};
4613 protected
4614 Integer num,actual,len;
4615 algorithm
4616 ✗ len := listLength(inList);
4617 ✗ for num in selList loop
4618 ✗ if num > 0 and num <= len then
4619 ✗ actual := listGet(inList,num);
4620 outList := actual::outList;
4621 end if;
4622 end for;
4623 end selectFromList;
4624
4625
4626 protected function deleteEntriesFromAdjacencyMatrix "Deletes given entries from matrix. Applicable on Adjacency and on transposed Adjacency.
4627 author: ptaeuber 2015-02"
4628 input BackendDAE.AdjacencyMatrix mUpdate;
4629 input BackendDAE.AdjacencyMatrix mHelp;
4630 input list<Integer> entries;
4631 protected
4632 Integer rowIndx;
4633 list<Integer> rowsIndx, row;
4634 algorithm
4635
2/2
✓ Branch 0 taken 68076 times.
✓ Branch 1 taken 66850 times.
134926 for entry in entries loop
4636 68076 rowsIndx := arrayGet(mHelp,entry);
4637
2/2
✓ Branch 0 taken 142486 times.
✓ Branch 1 taken 68076 times.
210562 for rowIndx in rowsIndx loop
4638 142486 row := arrayGet(mUpdate,rowIndx);
4639 142486 row := List.deleteMemberOnTrue(entry, row, intEq);
4640 142486 Array.replaceAtWithFill(rowIndx,row,row,mUpdate);
4641 end for;
4642 end for;
4643 end deleteEntriesFromAdjacencyMatrix;
4644
4645
4646 protected function deleteRowsFromAdjacencyMatrix "Deletes given rows from matrix. Applicable on Adjacency and on transposed Adjacency.
4647 author: ptaeuber 2015-02"
4648 input BackendDAE.AdjacencyMatrix mUpdate;
4649 input list<Integer> rows;
4650 algorithm
4651
2/2
✓ Branch 0 taken 2032 times.
✓ Branch 1 taken 806 times.
2838 for row in rows loop
4652 2032 Array.replaceAtWithFill(row,{},{},mUpdate);
4653 end for;
4654 end deleteRowsFromAdjacencyMatrix;
4655
4656
4657 protected function getVarsOfEqnsWithMostVars "find vars occurring in the equations with the
4658 most variables considering the current matching.
4659 author: ptaeuber"
4660 input list<Integer> inVars;
4661 input BackendDAE.AdjacencyMatrix mIn;
4662 input BackendDAE.AdjacencyMatrixT mtIn;
4663 output list<Integer> outVars = {};
4664 protected
4665 Integer size, maxSize = 0;
4666 list<Integer> eqns;
4667 array<Integer> eqn_size_arr;
4668 algorithm
4669 148 eqn_size_arr := arrayCreate(arrayLength(mIn),-1);
4670
1/2
✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
108199 for i in 1:arrayLength(mIn) loop
4671 108051 size := listLength(mIn[i]);
4672 108051 eqn_size_arr[i] := size;
4673 if size > maxSize then
4674 maxSize := size;
4675 end if;
4676 end for;
4677
4678
2/2
✓ Branch 0 taken 100068 times.
✓ Branch 1 taken 148 times.
100216 for var in inVars loop
4679 100068 eqns := mtIn[var];
4680
2/2
✓ Branch 0 taken 288277 times.
✓ Branch 1 taken 80442 times.
368719 for e in eqns loop
4681
2/2
✓ Branch 1 taken 19626 times.
✓ Branch 2 taken 268651 times.
288277 if eqn_size_arr[e] == maxSize then
4682 outVars := var :: outVars;
4683 19626 break;
4684 end if;
4685 end for;
4686 end for;
4687 148 GCExt.free(eqn_size_arr);
4688 end getVarsOfEqnsWithMostVars;
4689
4690
4691 protected function getVarsOccurringInMostEquations
4692 "find rows in transposed adjacency matrix enhanced with most nonzero
4693 elements and put the indexes of these rows in a list.
4694 author: ptaeuber"
4695 input BackendDAE.AdjacencyMatrixT mtIn;
4696 input list<Integer> inSelect;
4697 output Integer length = 0;
4698 output list<Integer> outLst = {};
4699 protected
4700 Integer length1;
4701 list<Integer> row;
4702 algorithm
4703
2/2
✓ Branch 0 taken 19670 times.
✓ Branch 1 taken 152 times.
19822 for sel in inSelect loop
4704 19670 row := arrayGet(mtIn, sel);
4705 19670 length1 := listLength(row);
4706
2/2
✓ Branch 0 taken 393 times.
✓ Branch 1 taken 19277 times.
19670 if intGt(length1,length) then
4707 length := length1;
4708 outLst := {sel};
4709 elseif intEq(length1,length) then
4710 outLst := sel::outLst;
4711 end if;
4712 end for;
4713 end getVarsOccurringInMostEquations;
4714
4715
4716 protected function getVarOccurringInMostEquations "find rows in transposed adjacency matrix enhanced with most nonzero
4717 elements. Return the first found row index with most entries.
4718 author: ptaeuber"
4719 input BackendDAE.AdjacencyMatrixT mtIn;
4720 input list<Integer> inSelect;
4721 output Integer length = 0;
4722 output list<Integer> outLst = {};
4723 protected
4724 Integer length1;
4725 list<Integer> row;
4726 algorithm
4727
2/2
✓ Branch 0 taken 10025 times.
✓ Branch 1 taken 3160 times.
13185 for sel in inSelect loop
4728 10025 row := arrayGet(mtIn, sel);
4729 10025 length1 := listLength(row);
4730
2/2
✓ Branch 0 taken 3421 times.
✓ Branch 1 taken 6604 times.
10025 if intGt(length1,length) then
4731 length := length1;
4732 outLst := {sel};
4733 end if;
4734 end for;
4735 end getVarOccurringInMostEquations;
4736
4737
4738 protected function findNEntries " find rows with n nonzero elements and
4739 put the indexes of these rows in a list.
4740 author: Waurich TUD 2012-10"
4741 input BackendDAE.AdjacencyMatrix mtIn;
4742 input list<Integer> inSelect;
4743 input Integer num;
4744 output list<Integer> outList = {};
4745 protected
4746 Integer length;
4747 list<Integer> row;
4748 algorithm
4749 ✗ for sel in inSelect loop
4750 ✗ row := arrayGet(mtIn, sel);
4751 ✗ length := listLength(row);
4752 ✗ if intEq(num,length) then
4753 outList := sel::outList;
4754 end if;
4755 end for;
4756 end findNEntries;
4757
4758 // =============================================================================
4759 // section for preOptModule >>recursiveTearing<<
4760 //
4761 // inline and repeat tearing
4762 // author: Vitalij Ruge
4763 // =============================================================================
4764
4765 public function recursiveTearing
4766 input BackendDAE.BackendDAE inDAE;
4767 output BackendDAE.BackendDAE outDAE;
4768 protected
4769 Boolean con;
4770 algorithm
4771
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
17 if Flags.getConfigInt(Flags.RTEARING) > 0 then
4772 17 (outDAE, con) := recursiveTearingMain(inDAE);
4773
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 17 times.
20 while con loop
4774 3 outDAE := tearingSystem(outDAE);
4775 3 (outDAE, con) := recursiveTearingMain(outDAE);
4776 end while;
4777 else
4778 outDAE := inDAE;
4779 end if;
4780 end recursiveTearing;
4781
4782 protected function recursiveTearingMain
4783 input BackendDAE.BackendDAE inDAE;
4784 output BackendDAE.BackendDAE outDAE;
4785 output Boolean update = false;
4786 protected
4787 list<BackendDAE.EqSystem> systlst_new = {};
4788 BackendDAE.Shared shared;
4789 AvlTreePathFunction.Tree funcs;
4790 BackendDAE.Variables vars, globalKnownVars;
4791 BackendDAE.StrongComponents comps;
4792 BackendDAE.EquationArray eqns;
4793 BackendDAE.StateSets stateSets;
4794 BackendDAE.BaseClockPartitionKind partitionKind;
4795
4796 BackendDAE.InnerEquations innerEquations;
4797 BackendDAE.InnerEquation innerEquation;
4798 Integer eqindex, vindex;
4799 list<Integer> residualequations;
4800 list<Integer> tearingvars;
4801 list<BackendDAE.Var> var_lst;
4802 BackendDAE.Var var;
4803 array<DAE.ComponentRef> tear_cr;
4804 list<DAE.ComponentRef> tear_cr_lst, all_vars = {};
4805 array<DAE.Exp> tear_exp;
4806 DAE.ComponentRef cr;
4807 BackendDAE.Equation eqn, eqn1;
4808 DAE.Exp rhs, lhs, rhs1, sumRhs, sumLhs, e, res;
4809 Integer n, i, j, m, index = 1;
4810 array<Option<BackendDAE.Equation>> optarr, optarr_res;
4811 array<Integer> indx_res, indx_eq, indx_var;
4812 Boolean tmp_update, isDer;
4813 BackendDAE.AdjacencyMatrix mm;
4814 Boolean maxSizeOne = Flags.getConfigInt(Flags.RTEARING) == 1;
4815 list<DAE.Exp> loopT, noLoopT;
4816 algorithm
4817 20 shared := inDAE.shared;
4818 20 BackendDAE.SHARED(functionTree=funcs, globalKnownVars=globalKnownVars) := shared;
4819 //BackendDump.bltdump("IN:", inDAE);
4820
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 for syst in inDAE.eqs loop
4821
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 BackendDAE.EQSYSTEM(orderedVars=vars,orderedEqs=eqns,matching=BackendDAE.MATCHING(comps=comps),stateSets=stateSets,partitionKind=partitionKind) := syst;
4822 40 (_, mm, _) := BackendDAEUtil.getAdjacencyMatrix(syst, BackendDAE.SPARSE(), SOME(funcs), BackendDAEUtil.isInitializationDAE(shared));
4823 tmp_update := false;
4824
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 20 times.
86 for comp in comps loop
4825
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 63 times.
66 if isTornsystem(comp, true, false) then
4826 // -----
4827
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 BackendDAE.TORNSYSTEM(BackendDAE.TEARINGSET(innerEquations = innerEquations, residualequations= residualequations, tearingvars=tearingvars)) := comp;
4828 3 n := listLength(innerEquations);
4829 3 m := listLength(residualequations);
4830
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if maxSizeOne and m > 1 then
4831 ✗ continue;
4832 end if;
4833 3 indx_res := arrayCreate(m,0);
4834 3 indx_var := arrayCreate(n,0);
4835 3 indx_eq := arrayCreate(n,0);
4836 i := 1;
4837 3 optarr := arrayCreate(n, NONE());
4838 3 update := true;
4839 tmp_update := true;
4840 // -----
4841
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 for innerEquation in innerEquations loop
4842
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
9 (eqindex, {vindex}, _) := BackendDAEUtil.getEqnAndVarsFromInnerEquation(innerEquation);
4843 9 var as BackendDAE.VAR(varName = cr) := BackendVariable.getVarAt(vars, vindex);
4844 all_vars := cr :: all_vars;
4845 9 arrayUpdate(indx_var,i,vindex);
4846 9 eqn := BackendEquation.get(eqns, eqindex);
4847
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if BackendVariable.isStateVar(var) then
4848 ✗ eqn := BackendEquation.solveEquation(eqn, Expression.expDer(Expression.crefExp(cr)), SOME(funcs));
4849 else
4850 9 eqn := BackendEquation.solveEquation(eqn, Expression.crefExp(cr), SOME(funcs));
4851 end if;
4852 9 arrayUpdate(optarr, i, SOME(eqn));
4853 9 eqns := BackendEquation.setAtIndex(eqns, eqindex, eqn);
4854 9 arrayUpdate(indx_eq, i , eqindex);
4855 9 i := i + 1;
4856
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 time.
9 if Flags.isSet(Flags.DUMP_RTEARING) then
4857 8 print("INeqn => " + BackendDump.equationString(eqn) + "[" + intString(i-1) + "]\n");
4858 end if;
4859 end for; //innerEquations
4860
4861 // -----
4862
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
6 var_lst := list(BackendVariable.getVarAt(vars, i) for i in tearingvars);
4863
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
6 tear_cr_lst := list(BackendVariable.varCref(vv) for vv in var_lst);
4864 3 tear_cr := listArray(tear_cr_lst);
4865 3 all_vars := listAppend(tear_cr_lst, all_vars);
4866 3 tear_exp := arrayCreate(m, DAE.RCONST(0.0));
4867 i := 1;
4868
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
6 for tcr in tear_cr loop
4869 3 arrayUpdate(tear_exp, i, Expression.crefExp(tcr));
4870 3 i := i +1;
4871 end for; //cr
4872 // -----
4873 3 optarr_res := arrayCreate(m, NONE());
4874
4875
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 for i in 1:m loop
4876
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 eqindex :: residualequations := residualequations;
4877 3 arrayUpdate(indx_res, i , eqindex);
4878 3 eqn := BackendEquation.get(eqns, eqindex);
4879
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
3 if Flags.isSet(Flags.DUMP_RTEARING) then
4880 2 print("INres => " + BackendDump.equationString(eqn) + "[" + intString(i) + "]\n");
4881 end if;
4882 3 arrayUpdate(optarr_res, i, SOME(eqn));
4883 end for;
4884
4885
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 for i in 1:n loop
4886
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
9 SOME(eqn) := arrayGet(optarr, i);
4887 9 rhs := BackendEquation.getEquationRHS(eqn);
4888 9 lhs := BackendEquation.getEquationLHS(eqn);
4889 9 (cr,isDer) := Expression.expOrDerCref(lhs);
4890
4891 //print("*****" + ExpressionBasics.printExpStr(lhs) + "= " + ExpressionBasics.printExpStr(rhs) + "*******\n");
4892
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
21 for j in (i+1):n loop
4893
2/2
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 6 times.
12 if listMember(arrayGet(indx_var,i) , arrayGet(mm, arrayGet(indx_eq,j))) then
4894
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
6 SOME(eqn1) := arrayGet(optarr, j);
4895 //print("\n (" + intString(i) + "," + intString(j) + ") => \n" + BackendDump.equationString(eqn1));
4896 6 rhs1 := BackendEquation.getEquationRHS(eqn1);
4897 6 rhs1 := recursiveTearingReplace(rhs1, cr, rhs, isDer);
4898 6 rhs1 := recursiveTearingCollect(tear_exp, rhs1);
4899 6 (index, vars, eqns, shared, _, e, _, _, _) := BackendDAEOptimize.simplifyLoopExp(index, vars, eqns, shared, all_vars, rhs1, {}, {}, true, true,-1,{}, "RTEARING");
4900 6 eqn1 := BackendEquation.setEquationRHS(eqn1, e);
4901 //print( "=>" + BackendDump.equationString(eqn1) + "\n");
4902 6 arrayUpdate(optarr,j,SOME(eqn1));
4903 end if;
4904 end for; // j
4905
4906 //res eqn
4907
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 for j in 1:m loop
4908
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 6 times.
9 if listMember(arrayGet(indx_var,i) , arrayGet(mm, arrayGet(indx_res,j))) then
4909
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 SOME(eqn1) := arrayGet(optarr_res, j);
4910
4911 3 res := BackendDAEOptimize.makeEquationToResidualExp(eqn1);
4912 3 res := recursiveTearingCollect(tear_exp, res);
4913 3 (loopT, noLoopT) := BackendDAEOptimize.simplifyLoops_SplitTerms(all_vars, res);
4914
4915 3 sumRhs := Expression.makeSum1(noLoopT,true);
4916 3 sumLhs := Expression.makeSum1(loopT,true);
4917
4918 3 sumRhs := recursiveTearingReplace(sumRhs, cr, rhs, isDer);
4919 3 sumLhs := recursiveTearingReplace(sumLhs, cr, rhs, isDer);
4920
4921 3 sumRhs := recursiveTearingCollect(tear_exp,sumRhs);
4922 3 sumLhs := recursiveTearingCollect(tear_exp,sumLhs);
4923
4924 // RHS
4925 3 (sumRhs,_) := ExpressionSimplify.simplify(sumRhs);
4926 3 (index, vars, eqns, shared, _, sumRhs, _, _, _) := BackendDAEOptimize.simplifyLoopExp(index, vars, eqns, shared, all_vars, sumRhs, {}, {}, true, true,-1,{}, "RTEARING");
4927 3 eqn1 := BackendEquation.setEquationRHS(eqn1, Expression.negate(sumRhs));
4928
4929 //LHS
4930 3 (sumLhs,_) := ExpressionSimplify.simplify(sumLhs);
4931 3 (index, vars, eqns, shared, _, sumLhs, _, _, _) := BackendDAEOptimize.simplifyLoopExp(index, vars, eqns, shared, all_vars, sumLhs, {}, {}, true, true,-1,{}, "RTEARING");
4932 3 eqn1 := BackendEquation.setEquationLHS(eqn1, sumLhs);
4933 3 arrayUpdate(optarr_res,j,SOME(eqn1));
4934 end if; // listMember
4935 end for;//j
4936 end for; // i
4937
4938
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
12 for i in 1:n loop
4939 9 eqindex := arrayGet(indx_eq,i);
4940
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
9 SOME(eqn) := arrayGet(optarr, i);
4941 9 eqns := BackendEquation.setAtIndex(eqns, eqindex, eqn);
4942
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 time.
9 if Flags.isSet(Flags.DUMP_RTEARING) then
4943 8 print("OUTeqn => " + BackendDump.equationString(eqn) + "[" + intString(i-1) + "]\n");
4944 end if;
4945 end for; // i
4946
4947
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 for i in 1:m loop
4948 3 eqindex := arrayGet(indx_res,i);
4949
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 SOME(eqn) := arrayGet(optarr_res, i);
4950 3 eqns := BackendEquation.setAtIndex(eqns, eqindex, eqn);
4951
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
3 if Flags.isSet(Flags.DUMP_RTEARING) then
4952 2 print("OUTres => " + BackendDump.equationString(eqn) + "[" + intString(i-1) + "]\n");
4953 end if;
4954 end for; // i
4955
4956
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
3 if Flags.isSet(Flags.DUMP_RTEARING) then
4957 2 print("****************\n");
4958
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 for i in 1:m loop
4959 2 print("TearVar: " + ExpressionBasics.printExpStr(arrayGet(tear_exp, i)) + "[" + intString(i-1) + "]\n");
4960 end for;
4961 2 print("****************\n");
4962 end if;
4963
4964 end if; // isTornsystem
4965 end for; // comp
4966
4967
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 17 times.
20 if tmp_update then
4968 3 systlst_new := BackendDAEUtil.createEqSystem(vars, eqns, stateSets, partitionKind) :: systlst_new;
4969 else
4970 systlst_new := syst :: systlst_new;
4971 end if;
4972
4973 end for; // syst
4974
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 17 times.
20 if update then
4975 3 outDAE := BackendDAE.DAE(systlst_new, shared);
4976 try
4977 3 outDAE := BackendDAEUtil.transformBackendDAE(outDAE,SOME((BackendDAE.NO_INDEX_REDUCTION(),BackendDAE.EXACT())),NONE(),NONE());
4978 else
4979 update := false;
4980 end try;
4981 else
4982 outDAE := inDAE;
4983 end if;
4984 //BackendDAE.DAE(systlst_new) := outDAE;
4985 //outDAE := BackendDAE.DAE(systlst_new, shared);
4986 //BackendDump.bltdump("OUT:", outDAE);
4987 end recursiveTearingMain;
4988
4989 protected function recursiveTearingCollect
4990 input array<DAE.Exp> tear_exp;
4991 input DAE.Exp inExp;
4992 output DAE.Exp outExp;
4993 protected
4994 Integer k;
4995 DAE.Exp lhs, e1, e2;
4996 algorithm
4997
4998 15 (e1, e2) := ExpressionSolve.collectX(inExp, arrayGet(tear_exp, 1));
4999
5000
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 for k in 2:arrayLength(tear_exp) loop
5001 ✗ (lhs, e2) := ExpressionSolve.collectX(e2, arrayGet(tear_exp, k));
5002 ✗ e1 := Expression.expAdd(e1, lhs);
5003 end for;
5004 15 outExp := Expression.expAdd(e2, e1);
5005 end recursiveTearingCollect;
5006
5007 protected function isTornsystem
5008 input BackendDAE.StrongComponent comp;
5009 input Boolean getLin = true;
5010 input Boolean getNoLin = false;
5011 output Boolean res;
5012
5013 algorithm
5014 res := match comp
5015 local Boolean linear;
5016
5017 case BackendDAE.TORNSYSTEM(linear=linear)
5018 guard linear == getLin or getNoLin == (not linear)
5019 then true;
5020 else false;
5021 end match;
5022 end isTornsystem;
5023
5024 protected function recursiveTearingHelper
5025 "
5026 collect expression
5027 "
5028 input DAE.Exp rhs1;
5029 input array<DAE.Exp> tear_exp;
5030 input Integer m;
5031 output DAE.Exp sumRhs = Expression.makeConstZeroE(rhs1);
5032 protected
5033 Integer k;
5034 DAE.Exp e, rhs = rhs1;
5035 algorithm
5036 ✗ for k in 1:m loop
5037 ✗ (e, rhs) := ExpressionSolve.collectX(rhs, arrayGet(tear_exp, k));
5038 ✗ sumRhs := Expression.expAdd(e, sumRhs);
5039 end for;
5040 ✗ sumRhs := Expression.expAdd(rhs, sumRhs);
5041 ✗ (sumRhs,_) := ExpressionSimplify.simplify(sumRhs);
5042 end recursiveTearingHelper;
5043
5044 protected function recursiveTearingReplace
5045 input DAE.Exp inExp;
5046 input DAE.ComponentRef inSourceExp;
5047 input DAE.Exp inTargetExp;
5048 input Boolean isDer;
5049 output DAE.Exp res;
5050 algorithm
5051
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if isDer then
5052 ✗ res := Expression.crefExp(inSourceExp);
5053 ✗ res := Expression.expDer(res);
5054 ✗ (res,_) := Expression.replaceExp(inExp, res, inTargetExp);
5055 else
5056 12 res := Expression.replaceCrefBottomUp(inExp, inSourceExp, inTargetExp);
5057 end if;
5058
5059 end recursiveTearingReplace;
5060
5061
5062 protected function getUnassigned
5063 input array<Integer> ass;
5064 output list<Integer> unassigned={};
5065 algorithm
5066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9422 times.
1521640 for i in 1:arrayLength(ass) loop
5067
2/2
✓ Branch 0 taken 865292 times.
✓ Branch 1 taken 646926 times.
1512218 if Dangerous.arrayGetNoBoundsChecking(ass, i) < 0 then
5068 unassigned := i::unassigned;
5069 end if;
5070 end for;
5071 end getUnassigned;
5072
5073
5074 protected function dumpTearingSetLocalIndexes
5075 input list<Integer> tVars,residuals,order;
5076 input array<Integer> ass2;
5077 input Integer size;
5078 input array<list<Integer>> mapEqnIncRow;
5079 input BackendDAE.Variables vars;
5080 input BackendDAE.EquationArray eqns;
5081 input String setString;
5082 protected
5083 list<String> s;
5084 algorithm
5085 3 print("\n" + BORDER + "\n* TEARING RESULTS" + setString + ":\n* (Local Indexes)\n*\n* No of equations in strong component: "+intString(size)+"\n");
5086 3 print("* No of tVars: "+intString(listLength(tVars))+"\n");
5087 3 print("*\n* tVars: "+ stringDelimitList(List.map(listReverse(tVars),intString),",") + "\n");
5088
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if Flags.isSet(Flags.ITERATION_VARS) then
5089 ✗ s := list("* " + intString(tVar) + ": " + BackendDump.varString(BackendVariable.getVarAt(vars,tVar)) for tVar in tVars);
5090 ✗ print(stringDelimitList(s, "\n") + "\n");
5091 end if;
5092 3 print("*\n* resEq: "+ stringDelimitList(List.map(residuals,intString),",") + "\n");
5093
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3 if Flags.isSet(Flags.ITERATION_VARS) and Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5094 ✗ s := list("* " + intString(eqn) + ": " + BackendDump.equationString(BackendEquation.get(eqns,eqn)) for eqn in residuals);
5095 ✗ print(stringDelimitList(s, "\n") + "\n");
5096 end if;
5097
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 3 times.
10 s := list("{" + intString(e) + ":" + stringDelimitList(List.map(List.map1r(mapEqnIncRow[e],arrayGet,ass2),intString),",") + "}" for e in order);
5098 3 print("*\n* innerEquations ({eqn,vars}):\n* " + stringDelimitList(s,", ") + "\n*\n" + BORDER + "\n\n");
5099 end dumpTearingSetLocalIndexes;
5100
5101
5102 protected function dumpTearingSetGlobalIndexes
5103 input BackendDAE.TearingSet tearingSet;
5104 input Integer size;
5105 input String setString;
5106 protected
5107 list<Integer> tVars,residuals;
5108 BackendDAE.InnerEquations innerEquations;
5109 algorithm
5110 24 BackendDAE.TEARINGSET(tearingvars=tVars,residualequations=residuals,innerEquations=innerEquations) := tearingSet;
5111 24 print("\n" + BORDER + "\n* TEARING RESULTS" + setString + ":\n* (Global Indexes)\n*\n* No of equations in strong component: "+intString(size)+"\n");
5112 24 print("* No of tVars: "+intString(listLength(tVars))+"\n");
5113 24 print("*\n* tVars: "+ stringDelimitList(List.map(listReverse(tVars),intString),",") + "\n");
5114 24 print("*\n* resEq: "+ stringDelimitList(List.map(residuals,intString),",") + "\n");
5115 24 print("*\n* innerEquations ({eqn,vars}):\n* " + stringDelimitList(List.map(innerEquations, BackendDump.innerEquationString),", ") + "\n*\n*" + BORDER + "\n\n");
5116 end dumpTearingSetGlobalIndexes;
5117
5118
5119 protected function dumpTearingSetsGlobalIndexes
5120 input list<BackendDAE.TearingSet> tearingSets;
5121 input Integer size;
5122 algorithm
5123
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1 time.
25 for tearingSet in tearingSets loop
5124 24 dumpTearingSetGlobalIndexes(tearingSet,size,"");
5125 end for;
5126 end dumpTearingSetsGlobalIndexes;
5127
5128
5129 // =============================================================================
5130 //
5131 // Total Tearing - Determination of All Possible Tearing Sets
5132 // author: ptaeuber FHB 2016
5133 //
5134 // =============================================================================
5135
5136 protected function totalTearing " determination of all possible tearing sets
5137 author: ptaeuber FHB 2016"
5138 input BackendDAE.EqSystem isyst;
5139 input BackendDAE.Shared ishared;
5140 input list<Integer> eindex;
5141 input list<Integer> vindx;
5142 input Option<list<tuple<Integer, Integer, BackendDAE.Equation>>> ojac;
5143 input BackendDAE.JacobianType jacType;
5144 input Boolean mixedSystem;
5145 output BackendDAE.StrongComponent ocomp;
5146 output Boolean outRunMatching;
5147 protected
5148 Integer size;
5149 array<Integer> ass1,ass2,mapIncRowEqn;
5150 array<list<Integer>> mapEqnIncRow;
5151 list<Integer> tVars,order,causEq,unsolvables,discreteVars;
5152 BackendDAE.EqSystem subsyst;
5153 BackendDAE.Variables vars;
5154 BackendDAE.EquationArray eqns;
5155 BackendDAE.AdjacencyMatrix m,mLoop;
5156 BackendDAE.AdjacencyMatrix mt,mtLoop;
5157 BackendDAE.AdjacencyMatrixEnhanced me;
5158 BackendDAE.AdjacencyMatrixTEnhanced meT;
5159 BackendDAE.BackendDAEType DAEtype;
5160 list<BackendDAE.TearingSet> tearingSets;
5161 list<BackendDAE.Equation> eqn_lst;
5162 list<BackendDAE.Var> var_lst;
5163 Boolean linear;
5164 String modelName;
5165 list<list<Integer>> powerSet={};
5166 list<tuple<array<Integer>,array<Integer>,list<Integer>>> matchingList;
5167 UnorderedSet<array<Integer>> visited;
5168 algorithm
5169 1 linear := BackendDAEUtil.getLinearfromJacType(jacType);
5170 BackendDAE.SHARED(backendDAEType=DAEtype, info=BackendDAE.EXTRA_INFO(fileNamePrefix=modelName)) := ishared;
5171
5172
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5173 ✗ print("\n" + BORDER + "\nBEGINNING of totalTearing\n\n");
5174 end if;
5175
5176 // Generate Subsystem to get the adjacency matrix
5177 1 size := listLength(vindx);
5178 1 eqn_lst := BackendEquation.getList(eindex,BackendEquation.getEqnsFromEqSystem(isyst));
5179 1 eqns := BackendEquation.listEquation(eqn_lst);
5180 1 var_lst := List.map1r(vindx, BackendVariable.getVarAt, BackendVariable.daeVars(isyst));
5181 1 vars := BackendVariable.listVar1(var_lst);
5182 1 subsyst := BackendDAEUtil.createEqSystem(vars, eqns);
5183 1 (subsyst,m,mt,_,_) := BackendDAEUtil.getAdjacencyMatrixScalar(subsyst, BackendDAE.NORMAL(),NONE(), BackendDAEUtil.isInitializationDAE(ishared));
5184
5185
5186 // Delete negative entries from adjacency matrix
5187 1 m := Array.map(m,deleteNegativeEntries);
5188 1 mt := Array.map(mt,deleteNegativeEntries);
5189
5190
2/4
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
1 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5191 ✗ print("\n\n###BEGIN print Strong Component#####################\n(Function:totalTearing)\n");
5192 ✗ BackendDump.printEqSystem(subsyst);
5193 ✗ print("\n###END print Strong Component#######################\n(Function:totalTearing)\n\n\n");
5194 end if;
5195
5196 // Get advanced adjacency matrix (determine how the variables occur in the equations)
5197 1 (me,meT,mapEqnIncRow,mapIncRowEqn) := BackendDAEUtil.getAdjacencyMatrixEnhancedScalar(subsyst,ishared,false);
5198
5199 // Determine unsolvable vars to consider solvability
5200 1 unsolvables := getUnsolvableVars(size,meT);
5201
5202
2/4
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
1 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5203 ✗ print("\nAdjacencyMatrixEnhanced:\n");
5204 ✗ BackendDump.dumpAdjacencyMatrixEnhanced(me);
5205 ✗ print("\nAdjacencyMatrixTransposedEnhanced:\n");
5206 ✗ BackendDump.dumpAdjacencyMatrixTEnhanced(meT);
5207 end if;
5208
5209
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5210 ✗ print("\n\nmapEqnIncRow:"); //+ stringDelimitList(List.map(List.flatten(arrayList(mapEqnIncRow)),intString),",") + "\n\n");
5211 ✗ BackendDump.dumpAdjacencyMatrix(mapEqnIncRow);
5212 ✗ print("\nmapIncRowEqn:\n" + stringDelimitList(List.mapArray(mapIncRowEqn, intString),",") + "\n\n");
5213 ✗ print("\n\nUNSOLVABLES:\n" + stringDelimitList(List.map(unsolvables,intString),",") + "\n\n");
5214 end if;
5215
5216 // Determine discrete vars
5217 1 discreteVars := findDiscrete(var_lst);
5218
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5219 ✗ print("\nDiscrete Vars:\n" + stringDelimitList(List.map(discreteVars,intString),",") + "\n\n");
5220 end if;
5221
5222
5223 // Determine all possible sets of tearing variables and save them in powerSet
5224 // ******************************************
5225
5226
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
16 for i in (Util.intPow(2,size)-1):-1:1 loop
5227 15 powerSet := getPowerSetElement(i)::powerSet;
5228 end for;
5229
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 if Flags.isSet(Flags.TOTAL_TEARING_DUMP) or Flags.isSet(Flags.TOTAL_TEARING_DUMPVERBOSE) then
5230 1 BackendDump.dumpListList(powerSet,"Power Set");
5231 end if;
5232
5233
5234
5235 // BEGIN TO LOOP
5236 // ******************************************
5237 tearingSets := {};
5238
5239
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 if Flags.isSet(Flags.TOTAL_TEARING_DUMP) or Flags.isSet(Flags.TOTAL_TEARING_DUMPVERBOSE) then
5240 1 print("\n\n###BEGIN TO LOOP#####################\n" + BORDER + "\n\n\n");
5241 end if;
5242
5243
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 time.
16 for tVars in powerSet loop
5244
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 if Flags.isSet(Flags.TOTAL_TEARING_DUMPVERBOSE) then
5245 ✗ print("\ntVars:\n" + stringDelimitList(List.map(tVars,intString),",") + "\n\n");
5246 end if;
5247
5248
5249 // Initialize matching
5250 15 ass1 := arrayCreate(size,-1);
5251 15 ass2 := arrayCreate(size,-1);
5252 order := {};
5253
5254 15 mLoop := arrayCopy(m);
5255 15 mtLoop := arrayCopy(mt);
5256
5257 // mark tVars in ass1
5258 15 markTVarsOrResiduals(tVars, ass1);
5259
5260 // remove tearing vars from adjacency matrix and transposed adjacency matrix
5261 15 deleteEntriesFromAdjacencyMatrix(mLoop, mtLoop, tVars);
5262 15 deleteRowsFromAdjacencyMatrix(mtLoop, tVars);
5263
5264 // initially find equations which can be causalized in the next step and save in causEq
5265 15 causEq := traverseCollectiveEqnsforAssignable(ass2,mLoop,mapEqnIncRow);
5266
5267 // if Flags.isSet(Flags.TOTAL_TEARING_DUMPVERBOSE) then
5268 // print("\nInitial ass1: " + stringDelimitList(List.mapArray(ass1, intString),",")+"\n");
5269 // print("Initial ass2: " + stringDelimitList(List.mapArray(ass2, intString),",") + "\n");
5270 // print("\nInitial m:");BackendDump.dumpAdjacencyMatrix(mLoop);
5271 // print("\nInitial mt:");BackendDump.dumpAdjacencyMatrix(mtLoop);
5272 // print("\nInitial causEq: " + stringDelimitList(List.map(causEq,intString),",") + "\n");
5273 // print("Initial order: " + stringDelimitList(List.map(order,intString),",") + "\n\n\n\n");
5274 // end if;
5275
5276
5277 // Find all possible matchings for this set of tearing variables.
5278 // The visited set memoizes already explored assignment states so that
5279 // states reachable via several causalization orders are only expanded once.
5280 15 visited := UnorderedSet.new(Array.hashIntArray, Array.isEqual);
5281 15 matchingList := totalMatching(ass1,ass2,order,causEq,mLoop,mtLoop,me,mapEqnIncRow,mapIncRowEqn,visited,{});
5282
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 if Flags.isSet(Flags.TOTAL_TEARING_DUMPVERBOSE) then
5283 ✗ dumpMatchingList(matchingList);
5284 end if;
5285
5286 // save tearing sets in list
5287 15 tearingSets := createTearingSets(tVars,matchingList,vindx,eindex,mapEqnIncRow,mapIncRowEqn,tearingSets);
5288
5289 end for;
5290
5291 // dump all different tearing sets
5292
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 if Flags.isSet(Flags.TOTAL_TEARING_DUMP) or Flags.isSet(Flags.TOTAL_TEARING_DUMPVERBOSE) then
5293 1 dumpTearingSetsGlobalIndexes(tearingSets,size);
5294 end if;
5295
5296 // Determine the rest of the information needed for BackendDAE.TORNSYSTEM
5297 // ***************************************************************************
5298
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
3 ocomp := BackendDAE.TORNSYSTEM(listHead(tearingSets),NONE(),linear,mixedSystem);
5299 outRunMatching := true;
5300
5301
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 if Flags.isSet(Flags.TOTAL_TEARING_DUMP) or Flags.isSet(Flags.TOTAL_TEARING_DUMPVERBOSE) then
5302 1 print("\n\nTotal number of different tearing sets: " + intString(listLength(tearingSets)) + "\n\n");
5303 end if;
5304
5305
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5306 ✗ print("\nEND of totalTearing\n" + BORDER + "\n\n");
5307 end if;
5308 end totalTearing;
5309
5310
5311 protected function getPowerSetElement
5312 input Integer i;
5313 output list<Integer> powerSetElement={};
5314 protected
5315 Integer c=0,e=i,r;
5316 algorithm
5317
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 15 times.
64 while not intEq(e,0) loop
5318
1/2
✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
49 c := c+1;
5319 r := intMod(e,2);
5320 49 e := intDiv(e,2);
5321
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 32 times.
49 if intEq(r,1) then
5322 powerSetElement := c :: powerSetElement;
5323 end if;
5324 end while;
5325 end getPowerSetElement;
5326
5327
5328 protected function totalMatching "function to find all possible matchings for one given set of tearing variables (DFS)
5329 author: ptaeuber FHB 2016"
5330 input array<Integer> ass1,ass2;
5331 input list<Integer> orderIn;
5332 input list<Integer> causEqIn;
5333 input BackendDAE.AdjacencyMatrix m;
5334 input BackendDAE.AdjacencyMatrixT mt;
5335 input BackendDAE.AdjacencyMatrixEnhanced me;
5336 input array<list<Integer>> mapEqnIncRow;
5337 input array<Integer> mapIncRowEqn;
5338 input UnorderedSet<array<Integer>> visited "already explored assignment states (ass1)";
5339 input list<tuple<array<Integer>,array<Integer>,list<Integer>>> matchingListIn;
5340 output list<tuple<array<Integer>,array<Integer>,list<Integer>>> matchingListOut=matchingListIn;
5341 protected
5342 list<Integer> order, causEq, e_exp, vars, unassigned;
5343 array<Integer> ass1Copy,ass2Copy;
5344 BackendDAE.AdjacencyMatrix mCopy;
5345 BackendDAE.AdjacencyMatrixT mtCopy;
5346 Boolean solvable;
5347 algorithm
5348
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 37 times.
111 for e in causEqIn loop
5349 // 1. Check solvability (read-only, no copies needed yet)
5350 74 (solvable, e_exp, vars) := eqnSolvableCheck(e, mapEqnIncRow, ass1, m, me);
5351
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 55 times.
74 if not solvable then
5352 19 continue;
5353 else
5354 // 2. Deep copies to avoid side effects: makeAssignment mutates the arrays
5355 // in place, but each sibling branch of the DFS needs the unmodified state
5356 55 ass1Copy := arrayCopy(ass1);
5357 55 ass2Copy := arrayCopy(ass2);
5358 55 mCopy := arrayCopy(m);
5359 55 mtCopy := arrayCopy(mt);
5360
5361 // 3. Match e_exp with corresponding variable(s), i.e.: update ass1, ass2, m, order
5362 55 makeAssignment(e_exp,vars,ass1Copy,ass2Copy,mCopy,mtCopy);
5363 order := e::orderIn;
5364
5365 // The new state (and thereby its whole subtree) is fully determined by the
5366 // assignments in ass1: skip it if it was already reached via a different
5367 // causalization order
5368
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 46 times.
55 if UnorderedSet.contains(ass1Copy, visited) then
5369 9 continue;
5370 end if;
5371 46 UnorderedSet.addNew(ass1Copy, visited);
5372
5373 // 4. Determine new possible causEq
5374 46 causEq := traverseCollectiveEqnsforAssignable(ass2Copy,mCopy,mapEqnIncRow);
5375
5376 // 5. Dump
5377 // if Flags.isSet(Flags.TOTAL_TEARING_DUMPVERBOSE) then
5378 // print("\nNew ass1: " + stringDelimitList(List.mapArray(ass1Copy, intString),",")+"\n");
5379 // print("New ass2: " + stringDelimitList(List.mapArray(ass2Copy, intString),",") + "\n");
5380 // print("\nNew m:");BackendDump.dumpAdjacencyMatrix(mCopy);
5381 // print("\nNew mt:");BackendDump.dumpAdjacencyMatrix(mtCopy);
5382 // print("\nNew causEq: " + stringDelimitList(List.map(causEq,intString),",") + "\n");
5383 // print("New order: " + stringDelimitList(List.map(order,intString),",") + "\n\n\n\n");
5384 // end if;
5385
5386 // 6. Decide what to do
5387
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 22 times.
46 if listEmpty(causEq) then
5388 // full matching found?
5389 24 unassigned := getUnassigned(ass1Copy);
5390
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if listEmpty(unassigned) then
5391 // save current full matching (it is new since the state was not visited before)
5392 24 matchingListOut := (ass1Copy,ass2Copy,listReverse(order)) :: matchingListOut;
5393 end if;
5394 else
5395 // Continue with matching
5396 22 matchingListOut := totalMatching(ass1Copy,ass2Copy,order,causEq,mCopy,mtCopy,me,mapEqnIncRow,mapIncRowEqn,visited,matchingListOut);
5397 end if;
5398 end if;
5399 end for;
5400 end totalMatching;
5401
5402
5403 protected function createTearingSets
5404 input list<Integer> tVarsIn;
5405 input list<tuple<array<Integer>,array<Integer>,list<Integer>>> matchingList;
5406 input list<Integer> vindx;
5407 input list<Integer> eindex;
5408 input array<list<Integer>> mapEqnIncRow;
5409 input array<Integer> mapIncRowEqn;
5410 input list<BackendDAE.TearingSet> tearingSetsIn;
5411 output list<BackendDAE.TearingSet> tearingSetsOut=tearingSetsIn;
5412 protected
5413 array<Integer> ass1,ass2;
5414 list<Integer> tVars,residual,residual_coll,order;
5415 BackendDAE.InnerEquations innerEquations;
5416 algorithm
5417
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 15 times.
39 for matching in matchingList loop
5418 24 (ass1,ass2,order) := matching;
5419
5420 // Unassigned equations are residual equations
5421 24 residual := getUnassigned(ass2);
5422 24 residual_coll := List.map1r(residual,arrayGet,mapIncRowEqn);
5423 24 residual_coll := List.unique(residual_coll);
5424
5425 // Convert indexes
5426 24 tVars := selectFromList_rev(vindx, tVarsIn);
5427 24 residual := selectFromList_rev(eindex, residual_coll);
5428
5429 // assign innerEquations:
5430 24 innerEquations := assignInnerEquations(order,eindex,vindx,ass2,mapEqnIncRow,NONE());
5431
5432 // Create BackendDAE.TearingSet for strict set
5433 24 tearingSetsOut := BackendDAE.TEARINGSET(tVars,residual,innerEquations,BackendDAE.EMPTY_JACOBIAN()) :: tearingSetsOut;
5434
5435
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 24 times.
24 if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5436 ✗ print("\nTearing Variables:\n" + stringDelimitList(List.map(tVarsIn,intString),",") +"\n");
5437 ✗ print("Residual Equations:\n" + stringDelimitList(List.map(residual_coll,intString),",") +"\n\n");
5438 end if;
5439 end for;
5440 end createTearingSets;
5441
5442
5443 protected function dumpMatchingList
5444 input list<tuple<array<Integer>,array<Integer>,list<Integer>>> matchingList;
5445 protected
5446 Integer c=0;
5447 list<Integer> order;
5448 array<Integer> ass1, ass2;
5449 algorithm
5450 ✗ print("\n");
5451 ✗ for matching in matchingList loop
5452 ✗ c := c+1;
5453 ✗ (ass1,ass2,order) := matching;
5454 ✗ print("Matching " + intString(c) + ":\n");
5455 ✗ print("ass1: " + stringDelimitList(List.mapArray(ass1, intString),",") + "\n");
5456 ✗ print("ass2: " + stringDelimitList(List.mapArray(ass2, intString),",") + "\n");
5457 ✗ print("order: " + stringDelimitList(List.map(order,intString),",") + "\n\n");
5458 end for;
5459 end dumpMatchingList;
5460
5461
5462
5463 // =============================================================================
5464 //
5465 // User-Defined Tearing - Determine the tearing set defined by the user
5466 // author: ptaeuber FHB 2016
5467 //
5468 // =============================================================================
5469
5470 protected function userDefinedTearing " determine the tearing set defined by the user
5471 author: ptaeuber FHB 2016"
5472 input BackendDAE.EqSystem isyst;
5473 input BackendDAE.Shared ishared;
5474 input list<Integer> eindex;
5475 input list<Integer> vindx;
5476 input Option<list<tuple<Integer, Integer, BackendDAE.Equation>>> ojac;
5477 input BackendDAE.JacobianType jacType;
5478 input Boolean mixedSystem;
5479 input list<Integer> userTVars;
5480 input list<Integer> userResiduals;
5481 output BackendDAE.StrongComponent ocomp;
5482 output Boolean outRunMatching;
5483 protected
5484 Integer size;
5485 array<Integer> ass1,ass2,mapIncRowEqn;
5486 array<list<Integer>> mapEqnIncRow;
5487 list<Integer> tVars,residuals,order,causEq,unsolvables,discreteVars,userResiduals_exp;
5488 BackendDAE.EqSystem subsyst;
5489 BackendDAE.Variables vars;
5490 BackendDAE.EquationArray eqns;
5491 BackendDAE.AdjacencyMatrix m;
5492 BackendDAE.AdjacencyMatrix mt;
5493 BackendDAE.AdjacencyMatrixEnhanced me;
5494 BackendDAE.AdjacencyMatrixTEnhanced meT;
5495 BackendDAE.BackendDAEType DAEtype;
5496 BackendDAE.InnerEquations innerEquations;
5497 BackendDAE.TearingSet tearingSet;
5498 list<BackendDAE.Equation> eqn_lst;
5499 list<BackendDAE.Var> var_lst;
5500 Boolean linear;
5501 String modelName;
5502 algorithm
5503
5504 ✗ linear := BackendDAEUtil.getLinearfromJacType(jacType);
5505 BackendDAE.SHARED(backendDAEType=DAEtype, info=BackendDAE.EXTRA_INFO(fileNamePrefix=modelName)) := ishared;
5506
5507 // Generate Subsystem to get the adjacency matrix
5508 ✗ size := listLength(vindx);
5509 ✗ eqn_lst := BackendEquation.getList(eindex,BackendEquation.getEqnsFromEqSystem(isyst));
5510 ✗ eqns := BackendEquation.listEquation(eqn_lst);
5511 ✗ var_lst := List.map1r(vindx, BackendVariable.getVarAt, BackendVariable.daeVars(isyst));
5512 ✗ vars := BackendVariable.listVar1(var_lst);
5513 ✗ subsyst := BackendDAEUtil.createEqSystem(vars, eqns);
5514 ✗ (subsyst,m,mt,_,_) := BackendDAEUtil.getAdjacencyMatrixScalar(subsyst, BackendDAE.NORMAL(),NONE(), BackendDAEUtil.isInitializationDAE(ishared));
5515
5516 // Delete negative entries from adjacency matrix
5517 ✗ m := Array.map(m,deleteNegativeEntries);
5518 ✗ mt := Array.map(mt,deleteNegativeEntries);
5519
5520 // Get advanced adjacency matrix (determine how the variables occur in the equations)
5521 ✗ (me,meT,mapEqnIncRow,mapIncRowEqn) := BackendDAEUtil.getAdjacencyMatrixEnhancedScalar(subsyst,ishared,false);
5522
5523 // Expand algorithm or equation array residual equations
5524 try
5525 ✗ userResiduals_exp := List.flatten(list(arrayGet(mapEqnIncRow, i) for i in userResiduals));
5526 else
5527 ✗ Error.addMessage(Error.USER_DEFINED_TEARING_ERROR, {"Index out of bounds."});
5528 ✗ fail();
5529 end try;
5530
5531
5532 // Dumps
5533 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5534 ✗ print("\n" + BORDER + "\nBEGINNING of userDefinedTearing\n\n");
5535 end if;
5536
5537 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5538 ✗ print("\nUsers tearing vars: " + stringDelimitList(List.map(userTVars,intString),",") + "\n");
5539 ✗ print("\nUsers residual equations: " + stringDelimitList(List.map(userResiduals,intString),",") + "\n");
5540 ✗ print("\nUsers residual equations expanded: " + stringDelimitList(List.map(userResiduals_exp,intString),",") + "\n");
5541 ✗ print("\n\n###BEGIN print Strong Component#####################\n(Function:userDefinedTearing)\n");
5542 ✗ BackendDump.printEqSystem(subsyst);
5543 ✗ print("\n###END print Strong Component#######################\n(Function:userDefinedTearing)\n\n\n");
5544 end if;
5545
5546 ✗ if not intEq(listLength(userTVars),listLength(userResiduals_exp)) then
5547 ✗ Error.addMessage(Error.USER_DEFINED_TEARING_ERROR, {"The number of tearing variables and residual equations is not identical."});
5548 ✗ fail();
5549 end if;
5550
5551 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5552 ✗ print("\nAdjacencyMatrixEnhanced:\n");
5553 ✗ BackendDump.dumpAdjacencyMatrixEnhanced(me);
5554 ✗ print("\nAdjacencyMatrixTransposedEnhanced:\n");
5555 ✗ BackendDump.dumpAdjacencyMatrixTEnhanced(meT);
5556 end if;
5557
5558 // Determine unsolvable vars to consider solvability
5559 ✗ unsolvables := getUnsolvableVars(size,meT);
5560
5561 // Determine discrete vars
5562 ✗ discreteVars := findDiscrete(var_lst);
5563
5564 // Dumps
5565 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5566 ✗ print("\n\nmapEqnIncRow:"); //+ stringDelimitList(List.map(List.flatten(arrayList(mapEqnIncRow)),intString),",") + "\n\n");
5567 ✗ BackendDump.dumpAdjacencyMatrix(mapEqnIncRow);
5568 ✗ print("\nmapIncRowEqn:\n" + stringDelimitList(List.mapArray(mapIncRowEqn, intString),",") + "\n\n");
5569 ✗ print("\n\nUNSOLVABLES:\n" + stringDelimitList(List.map(unsolvables,intString),",") + "\n\n");
5570 ✗ print("\nDiscrete Vars:\n" + stringDelimitList(List.map(discreteVars,intString),",") + "\n\n");
5571 end if;
5572
5573 // Initialize matching
5574 ✗ ass1 := arrayCreate(size,-1);
5575 ✗ ass2 := arrayCreate(size,-1);
5576 order := {};
5577
5578 // mark userTVars in ass1
5579 ✗ markTVarsOrResiduals(userTVars, ass1);
5580 // mark userResiduals in ass2
5581 ✗ markTVarsOrResiduals(userResiduals_exp, ass2);
5582
5583 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5584 ✗ print("\nass1: " + stringDelimitList(List.mapArray(ass1, intString),",") + "\n");
5585 ✗ print("ass2: " + stringDelimitList(List.mapArray(ass2, intString),",") + "\n");
5586 end if;
5587
5588 // remove tearing vars from adjacency matrix and transposed adjacency matrix
5589 ✗ deleteEntriesFromAdjacencyMatrix(m, mt, userTVars);
5590 ✗ deleteRowsFromAdjacencyMatrix(mt, userTVars);
5591 // remove residual equations from adjacency matrix and transposed adjacency matrix
5592 ✗ deleteEntriesFromAdjacencyMatrix(mt, m, userResiduals_exp);
5593 ✗ deleteRowsFromAdjacencyMatrix(m, userResiduals_exp);
5594
5595 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5596 ✗ print("\nAdjacency Matrix without tvars and residuals:\n");
5597 ✗ BackendDump.dumpAdjacencyMatrix(m);
5598 ✗ BackendDump.dumpAdjacencyMatrix(mt);
5599 end if;
5600
5601 ✗ if intEq(listLength(userTVars), countEmptyRows(m)) and intEq(listLength(userResiduals_exp), countEmptyRows(mt)) then
5602 // Find initial causalizable equations
5603 ✗ causEq := traverseCollectiveEqnsforAssignable(ass2,m,mapEqnIncRow);
5604
5605 // Call the matching algorithm
5606 ✗ order := simpleMatching(ass1,ass2,order,causEq,m,mt,me,mapEqnIncRow,mapIncRowEqn);
5607
5608 // Convert indexes
5609 ✗ tVars := selectFromList_rev(vindx, userTVars);
5610 ✗ residuals := selectFromList_rev(eindex, userResiduals);
5611
5612 // assign innerEquation:
5613 ✗ innerEquations := assignInnerEquations(order,eindex,vindx,ass2,mapEqnIncRow,NONE());
5614
5615 ✗ tearingSet := BackendDAE.TEARINGSET(tVars,residuals,innerEquations,BackendDAE.EMPTY_JACOBIAN());
5616
5617 // Create BackendDAE.TornSystem
5618 ✗ ocomp := BackendDAE.TORNSYSTEM(tearingSet,NONE(),linear,mixedSystem);
5619 outRunMatching := true;
5620
5621 // dump results with local indexes
5622 ✗ if Flags.isSet(Flags.TEARING_DUMP) or Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5623 ✗ dumpTearingSetLocalIndexes(userTVars,userResiduals,order,ass2,size,mapEqnIncRow,vars,eqns,"");
5624 end if;
5625
5626 // dump results with global indexes
5627 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5628 ✗ dumpTearingSetGlobalIndexes(tearingSet,size,"");
5629 end if;
5630
5631 else
5632 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5633 ✗ print("\nMatching failed, choose different tearing set!\n\n\n");
5634 end if;
5635 ✗ Error.addCompilerError("There is no possible matching for a user-defined tearing set.");
5636 ✗ fail();
5637
5638 end if;
5639
5640 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5641 ✗ print("\nEND of userDefinedTearing\n" + BORDER + "\n\n");
5642 end if;
5643 end userDefinedTearing;
5644
5645
5646 protected function countEmptyRows
5647 "function to count empty rows in adjacency matrices"
5648 input BackendDAE.AdjacencyMatrix m;
5649 output Integer count=0;
5650 algorithm
5651 ✗ for row in m loop
5652 ✗ if listEmpty(row) then
5653 ✗ count := count + 1;
5654 end if;
5655 end for;
5656 end countEmptyRows;
5657
5658
5659 protected function simpleMatching "function tries to find a matching based on a given set of tearing variables and residual equations
5660 author: ptaeuber FHB 2016"
5661 input array<Integer> ass1,ass2;
5662 input list<Integer> orderIn;
5663 input list<Integer> causEqIn;
5664 input BackendDAE.AdjacencyMatrix m;
5665 input BackendDAE.AdjacencyMatrixT mt;
5666 input BackendDAE.AdjacencyMatrixEnhanced me;
5667 input array<list<Integer>> mapEqnIncRow;
5668 input array<Integer> mapIncRowEqn;
5669 output list<Integer> orderOut=orderIn;
5670 protected
5671 Integer e;
5672 list<Integer> causEq=causEqIn, e_exp, vars;
5673 algorithm
5674 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5675 ✗ print("\nStart Matching:\n"+ UNDERLINE + "\n");
5676 end if;
5677 ✗ while not listEmpty(causEq) loop
5678 try
5679 ✗ (e,e_exp,vars) := getNextSolvableEqn(causEq,m,me,ass1,ass2,mapEqnIncRow,mapIncRowEqn,ass1);
5680 else
5681 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5682 ✗ print("\nMatching failed, choose different tearing set!\n\n\n");
5683 end if;
5684 ✗ Error.addCompilerError("There is no possible matching for a user-defined tearing set.");
5685 ✗ fail();
5686 end try;
5687
5688 // Dump
5689 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5690 ✗ print("causEq: " + stringDelimitList(List.map(causEq,intString),",") + "\nProcess " + intString(e) + ":\ne_exp: " + stringDelimitList(List.map(e_exp,intString),",") + "\n");
5691 end if;
5692
5693 // Match e_exp with corresponding variable(s), i.e.: update ass1, ass2, m, order
5694 ✗ makeAssignment(e_exp,vars,ass1,ass2,m,mt);
5695 ✗ orderOut := e::orderOut;
5696
5697 // Determine new possible causEq
5698 ✗ causEq := traverseCollectiveEqnsforAssignable(ass2,m,mapEqnIncRow);
5699 end while;
5700 ✗ if listEmpty(getUnassigned(ass1)) then
5701 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5702 ✗ print("\nMatching succeeded!\n");
5703 end if;
5704 ✗ orderOut := listReverse(orderOut);
5705 else
5706 ✗ if Flags.isSet(Flags.TEARING_DUMPVERBOSE) then
5707 ✗ print("\nMatching failed, choose different tearing set!\n\n\n");
5708 end if;
5709 ✗ Error.addCompilerError("There is no possible matching for a user-defined tearing set.");
5710 ✗ fail();
5711 end if;
5712 end simpleMatching;
5713
5714
5715 annotation(__OpenModelica_Interface="backend");
5716 end Tearing;
5717