Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 81.2% 446 / 0 / 549
Functions: -% 0 / 1 / 1
Branches: 69.1% 525 / 0 / 760

OMCompiler/Compiler/NBackEnd/Modules/3_Post/NBTearing.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 uniontype NBTearing
37 "file: NBTearing.mo
38 package: NBTearing
39 description: This file contains the data-types used for tearing. It is a
40 uniontype and therefore also contains some structures for tearing.
41 "
42
43 public
44 import BackendDAE = NBackendDAE;
45 import Module = NBModule;
46 import Slice = NBSlice;
47 import NBVariable.{VarSlice, VariablePointer, VariablePointers, VarData};
48 import NBEquation.{Equation, EqnSlice, EquationPointer, EquationPointers, EqData, EquationAttributes};
49 import StrongComponent = NBStrongComponent;
50
51 protected
52 // selfimport
53 import Tearing = NBTearing;
54
55 // OF imports
56 import Absyn.Path;
57
58 // NF imports
59 import Algorithm = NFAlgorithm;
60 import Expression = NFExpression;
61 import NFFunction.Function;
62 import Variable = NFVariable;
63 import ComponentRef = NFComponentRef;
64 import Subscript = NFSubscript;
65 import Type = NFType;
66 import Dimension = NFDimension;
67 import Operator = NFOperator;
68 import SimplifyExp = NFSimplifyExp;
69 import NFInstNode.InstNode;
70 import NFBackendExtension.{BackendInfo, VariableKind};
71
72 // Backend imports
73 import Adjacency = NBAdjacency;
74 import NBAdjacency.Solvability;
75 import Causalize = NBCausalize;
76 import BEquation = NBEquation;
77 import Initialization = NBInitialization;
78 import BJacobian = NBJacobian;
79 import BVariable = NBVariable;
80 import Differentiate = NBDifferentiate;
81 import Inline = NBInline;
82 import Jacobian = NBackendDAE.BackendDAE;
83 import Matching = NBMatching;
84 import Solve = NBSolve;
85 import Sorting = NBSorting;
86 import Partition = NBPartition;
87 import Resizable = NBResizable;
88 import NBResizable.EvalOrder;
89 import NBEquation.{Iterator, EquationKind};
90
91 //Util imports
92 import BackendUtil = NBBackendUtil;
93 import StringUtil;
94 import ErrorExt;
95
96 public
97
98 record TEARING_SET
99 list<Slice<VariablePointer>> iteration_vars "the variables used for iteration";
100 list<Slice<EquationPointer>> residual_eqns "implicitely solved residual equations";
101 array<StrongComponent> innerEquations "array of matched equations and variables";
102 Option<Jacobian> jac "optional jacobian";
103 end TEARING_SET;
104
105 function hash
106 "compute hash value independent of ordering by adding hash of iteration variables, should be unique enough"
107 input Tearing set;
108 output Integer h = sum(Slice.hash(var, BVariable.hash) for var in set.iteration_vars);
109 end hash;
110
111 function isEqual
112 "checking the jacobian should not be necessary"
113 input Tearing set1;
114 input Tearing set2;
115 output Boolean b;
116 algorithm
117 49 b := UnorderedSet.equal_list(set1.residual_eqns, set2.residual_eqns, function Slice.hash(func = Equation.hash), function Slice.isEqual(func = Equation.isEqualPtr));
118
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 14 times.
49 b := if b then Array.isEqualOnTrue(set1.innerEquations, set2.innerEquations, StrongComponent.isEqual) else b; // TODO inner equations don't need to be sorted identically
119
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 28 times.
49 b := if b then UnorderedSet.equal_list(set1.iteration_vars, set2.iteration_vars, function Slice.hash(func = BVariable.hash), function Slice.isEqual(func = BVariable.equalName)) else b;
120 end isEqual;
121
122 function size
123 input Tearing set;
124 input Boolean resize;
125 output Integer s;
126 algorithm
127
5/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
8 s := sum(Slice.size(eq, function Equation.size(resize = resize)) for eq in set.residual_eqns);
128
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 4 times.
11 s := s + sum(StrongComponent.size(eq, resize) for eq in set.innerEquations);
129 end size;
130
131 function toString
132 input Tearing set;
133 input output String str;
134 algorithm
135 4 str := StringUtil.headline_4(str);
136 4 str := str + "### Iteration Variables:\n" + Slice.lstToString(set.iteration_vars, BVariable.pointerToString, " ");
137 4 str := str + "\n### Residual Equations:\n" + Slice.lstToString(set.residual_eqns, function Equation.pointerToString(str = " "));
138 4 str := str + "\n### Inner Equations:\n" + Array.toString(set.innerEquations, function StrongComponent.toString(index = -1), "", " ", "\n ", "");
139
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 time.
4 if isSome(set.jac) then
140 3 str := str + "\n" + BJacobian.toString(Util.getOption(set.jac), "NLS");
141 end if;
142 end toString;
143
144 function main
145 "Wrapper function for any tearing function. This will be
146 called during simulation and gets the corresponding subfunction from
147 Config."
148 extends Module.wrapper;
149 input Partition.Kind kind;
150 protected
151 constant list<Module.tearingInterface> funcs = getModule();
152 Pointer<list<Pointer<Variable>>> new_vars = Pointer.create({});
153 algorithm
154
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 377 times.
377 if Flags.isSet(Flags.TEARING_DUMP) then
155 ✗ print(StringUtil.headline_1("[" + Partition.Partition.kindToString(kind) + "] Tearing") + "\n");
156 end if;
157 bdae := match (kind, bdae)
158 local
159 list<Partition.Partition> partitions;
160 Pointer<Integer> eq_index;
161
162 case (NBPartition.Kind.ODE, BackendDAE.MAIN(eqData = BEquation.EQ_DATA_SIM(uniqueIndex = eq_index)))
163 algorithm
164 188 bdae.ode := tearingTraverser(bdae.ode, funcs, bdae.funcMap, eq_index, kind, new_vars);
165 then bdae;
166
167 case (_, BackendDAE.MAIN(eqData = BEquation.EQ_DATA_SIM(uniqueIndex = eq_index))) guard(Partition.kindIsInitial(kind))
168 algorithm
169 188 bdae.init := tearingTraverser(bdae.init, funcs, bdae.funcMap, eq_index, kind, new_vars);
170
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 182 times.
188 if isSome(bdae.init_0) then
171 12 bdae.init_0 := SOME(tearingTraverser(Util.getOption(bdae.init_0), funcs, bdae.funcMap, eq_index, kind, new_vars));
172 end if;
173 then bdae;
174
175 case (NBPartition.Kind.DAE, BackendDAE.MAIN(dae = SOME(partitions), eqData = BEquation.EQ_DATA_SIM(uniqueIndex = eq_index)))
176 algorithm
177 1 bdae.dae := SOME(tearingTraverser(partitions, funcs, bdae.funcMap, eq_index, kind, new_vars));
178 // recursively call this function to also apply to the ODE section (used for events)
179 // ToDo: only create event partitions, disregard rest
180 1 then main(bdae, NBPartition.Kind.ODE);
181
182 // ToDo: all the other cases: e.g. Jacobian, Hessian
183 end match;
184
185 // new whole arrays for partial resizable iteration variables
186 bdae := match bdae
187 case BackendDAE.MAIN() guard not listEmpty(Pointer.access(new_vars)) algorithm
188 4 bdae.varData := VarData.addTypedList(bdae.varData, Pointer.access(new_vars), VarData.VarType.ALGEBRAIC);
189 then bdae;
190 else bdae;
191 end match;
192 end main;
193
194 function implicit
195 input output StrongComponent comp "suspected algebraic loop";
196 input UnorderedMap<Path, Function> funcMap "Function call bodies";
197 input output Integer index "current unique loop index";
198 input Partition.Kind kind = NBPartition.Kind.ODE "partition type";
199 protected
200 // dummy adjacency matrix, don't need it for implicit
201 Adjacency.Matrix dummy = Adjacency.EMPTY(NBAdjacency.MatrixStrictness.FULL);
202 StrongComponent new_comp;
203 Pointer<Boolean> homotopy = Pointer.create(false);
204 algorithm
205 (comp, dummy, index) := match comp
206 // create implicit equations
207 case StrongComponent.SINGLE_COMPONENT() algorithm
208 10 Equation.map(Pointer.access(comp.eqn), function Initialization.containsHomotopyCall(b = homotopy));
209
1/2
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
20 new_comp := StrongComponent.ALGEBRAIC_LOOP(
210 idx = index,
211 strict = singleImplicit(comp.var, comp.eqn),
212 casual = NONE(),
213 // a multi-dimensional var (e.g. matrix-coupled array equation like A*x=b with
214 // A a parameter matrix) can be genuinely linear even though it isn't solvable
215 // one scalar element at a time -- check like SLICED_COMPONENT does instead of
216 // always assuming nonlinear.
217 linear = isLinearSlice(comp.eqn, ComponentRef.scalarize(BVariable.getVarName(comp.var), false), funcMap),
218 mixed = false,
219 homotopy = Pointer.access(homotopy),
220 status = NBSolve.Status.IMPLICIT,
221 implicitlyCreated = true);
222 10 index := index + 1;
223 10 then finalize(new_comp, dummy, funcMap, index, VariablePointers.empty(), EquationPointers.empty(), Pointer.create(0), kind);
224
225 case StrongComponent.MULTI_COMPONENT() algorithm
226 ✗ Equation.map(Pointer.access(Slice.getT(comp.eqn)), function Initialization.containsHomotopyCall(b = homotopy));
227 ✗ new_comp := StrongComponent.ALGEBRAIC_LOOP(
228 idx = index,
229 strict = singleImplicit(Slice.getT(listHead(comp.vars)), Slice.getT(comp.eqn)), // this is wrong! need to take all vars
230 casual = NONE(),
231 linear = false,
232 mixed = false,
233 homotopy = Pointer.access(homotopy),
234 status = NBSolve.Status.IMPLICIT,
235 implicitlyCreated = true);
236 ✗ index := index + 1;
237 ✗ then finalize(new_comp, dummy, funcMap, index, VariablePointers.empty(), EquationPointers.empty(), Pointer.create(0), kind);
238
239 case StrongComponent.RESIZABLE_COMPONENT() algorithm
240 ✗ Equation.map(Pointer.access(Slice.getT(comp.eqn)), function Initialization.containsHomotopyCall(b = homotopy));
241 ✗ new_comp := StrongComponent.ALGEBRAIC_LOOP(
242 idx = index,
243 strict = singleImplicit(Slice.getT(comp.var), Slice.getT(comp.eqn)),
244 casual = NONE(),
245 linear = isLinearSlice(Slice.getT(comp.eqn), ComponentRef.scalarize(comp.var_cref, false), funcMap),
246 mixed = false,
247 homotopy = Pointer.access(homotopy),
248 status = NBSolve.Status.IMPLICIT,
249 implicitlyCreated = true);
250 ✗ index := index + 1;
251 ✗ then finalize(new_comp, dummy, funcMap, index, VariablePointers.empty(), EquationPointers.empty(), Pointer.create(0), kind);
252
253 // a component matched to a genuine partial array slice (comp.var/comp.eqn already
254 // carry the correct .indices) that could not be solved explicitly, e.g. a torn
255 // matrix-shaped subsystem for i_s[{1, 2}]. Same treatment as
256 // SINGLE_COMPONENT/RESIZABLE_COMPONENT, keeping the slice instead of wrapping a
257 // whole variable/equation. Was previously missing, falling through to "do nothing".
258 case StrongComponent.SLICED_COMPONENT() algorithm
259 ✗ Equation.map(Pointer.access(Slice.getT(comp.eqn)), function Initialization.containsHomotopyCall(b = homotopy));
260 ✗ new_comp := StrongComponent.ALGEBRAIC_LOOP(
261 idx = index,
262 strict = slicedImplicit(comp.var, comp.eqn),
263 casual = NONE(),
264 linear = isLinearSlice(Slice.getT(comp.eqn), ComponentRef.scalarize(comp.var_cref, false), funcMap),
265 mixed = false,
266 homotopy = Pointer.access(homotopy),
267 status = NBSolve.Status.IMPLICIT,
268 implicitlyCreated = true);
269 ✗ index := index + 1;
270 ✗ then finalize(new_comp, dummy, funcMap, index, VariablePointers.empty(), EquationPointers.empty(), Pointer.create(0), kind);
271
272 // do nothing otherwise
273 75 else (comp, dummy, index);
274 end match;
275 end implicit;
276
277 function singleImplicit
278 input VariablePointer var;
279 input EquationPointer eqn;
280 output NBTearing tearingSet = Tearing.TEARING_SET(
281 iteration_vars = {Slice.SLICE(var, {})},
282 residual_eqns = {Slice.SLICE(eqn, {})},
283 innerEquations = listArray({}),
284 jac = NONE());
285 end singleImplicit;
286
287 function slicedImplicit
288 "same as singleImplicit, but var already carries the correct .indices (see the
289 SLICED_COMPONENT case in implicit()) and must not be re-wrapped as a whole slice.
290 eqn is expanded to one residual_eqns entry per scalar row (see scalarSlices):
291 NBJacobian.compJacobian pulls one residual variable per residual_eqns entry
292 (Equation.getResidualVar), so a single entry covering all rows of a multi-row
293 array equation undercounts the residual side against a per-element iteration_vars
294 seed list, producing an empty/mismatched Jacobian sparsity pattern."
295 input Slice<VariablePointer> var;
296 input Slice<EquationPointer> eqn;
297 output NBTearing tearingSet = Tearing.TEARING_SET(
298 iteration_vars = {var},
299 residual_eqns = scalarSlices(eqn),
300 innerEquations = listArray({}),
301 jac = NONE());
302 end slicedImplicit;
303
304 function scalarSlices
305 "expands a (possibly whole, .indices={}) equation slice into one Slice entry per
306 scalar row, each wrapping its OWN, newly created scalar residual equation with a
307 distinct residual variable -- NOT just the same underlying equation pointer
308 re-sliced. Equations are keyed by their residual variable's name throughout this
309 codebase (Equation.getEqnName/getResidualVar), which is a whole-variable property
310 like everything else keyed that way here; multiple slices of one multi-row array
311 equation would all resolve back to the SAME residual variable and collapse to one
312 entry in any name-keyed collection built from them (e.g. EquationPointers.fromList
313 in NBJacobian.jacobianNumeric's adjacency-matrix build), silently undercounting
314 rows and producing an empty/wrong Jacobian sparsity pattern."
315 input Slice<EquationPointer> eqn;
316 output list<Slice<EquationPointer>> slices;
317 protected
318 Pointer<Equation> eqn_ptr = Slice.getT(eqn);
319 Equation e = Pointer.access(eqn_ptr);
320 list<Integer> indices = eqn.indices;
321 ComponentRef base_cref;
322 Expression residual;
323 Type elem_ty;
324 EquationAttributes attr;
325 list<Subscript> subs;
326 ComponentRef row_cref;
327 Pointer<Variable> row_var;
328 Variable row_var_data;
329 Expression row_residual;
330 Equation row_eqn;
331 Boolean is_for;
332 algorithm
333
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 2 times.
15 if listEmpty(indices) then
334
2/2
✓ Branch 1 taken 57 times.
✓ Branch 2 taken 13 times.
70 indices := list(i for i in 0:(Equation.size(eqn_ptr) - 1));
335 end if;
336
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
15 if List.hasOneElement(indices) and Equation.size(eqn_ptr) == 1 then
337 // already scalar: no row-collapse risk. A single row of a bigger array
338 // equation still has the residual variable of the whole array.
339 ✗ slices := {eqn};
340 else
341 15 base_cref := Equation.getEqnName(eqn_ptr);
342 15 is_for := Equation.isSingleBodyFor(e);
343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 residual := if is_for then Expression.EMPTY(Type.UNKNOWN()) else Equation.getResidualExp(e);
344 15 elem_ty := Type.arrayElementType(Expression.typeOf(residual));
345 15 attr := Equation.getAttributes(e);
346 15 attr.residual := true;
347 slices := {};
348
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 15 times.
80 for i in indices loop
349
1/2
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
65 if is_for then
350 65 row_residual := Equation.forArrayBodyRowResidual(e, i);
351 65 elem_ty := Type.arrayElementType(Expression.typeOf(row_residual));
352 else
353 ✗ subs := list(Subscript.INDEX(Expression.INTEGER(l + 1)) for l in Slice.indexToLocation(i, Equation.sizes(eqn_ptr)));
354 ✗ row_residual := Expression.applySubscripts(subs, residual);
355 end if;
356 65 (row_var, row_cref) := BVariable.makeAuxVar(ComponentRef.toString(base_cref), i, elem_ty, false);
357 // makeAuxVar tags the new var with a plain VariableKind derived from its type;
358 // it must instead be marked RESIDUAL_VAR like any other residual variable, or
359 // downstream Jacobian construction (NBJacobian.getTmpFilterFunction's
360 // BVariable.isResidual check for LS/NLS/DAE) won't recognize it as a result row
361 // and the Jacobian's sparsity pattern will come out empty for this equation.
362 65 row_var_data := Pointer.access(row_var);
363 65 row_var_data.backendinfo := BackendInfo.setVarKind(row_var_data.backendinfo, VariableKind.RESIDUAL_VAR());
364 65 Pointer.update(row_var, row_var_data);
365 65 attr.residualVar := SOME(row_var);
366 65 row_eqn := Equation.SCALAR_EQUATION(elem_ty, Expression.fromCref(row_cref), row_residual, Equation.getSource(e), attr);
367 65 slices := Slice.SLICE(Pointer.create(row_eqn), {}) :: slices;
368 end for;
369 15 slices := listReverse(slices);
370 end if;
371 end scalarSlices;
372
373 function containsNamedCref
374 input Expression exp;
375 input UnorderedSet<ComponentRef> names;
376 input output Boolean b;
377 algorithm
378 b := match (b, exp)
379 9 case (false, Expression.CREF()) then UnorderedSet.contains(ComponentRef.stripSubscriptsAll(exp.cref), names);
380 else b;
381 end match;
382 end containsNamedCref;
383
384 function isLinearSlice
385 "local, differentiation-based linearity check for a single equation against a
386 list of crefs, for use where no adjacency-matrix solvability info is available
387 (see checkLinearity for the normal, matrix-based version). Linear iff no
388 partial derivative w.r.t. one of the crefs still contains any of them --
389 catches both self- (x^2) and cross- (x*y) nonlinearity."
390 input Pointer<Equation> eqn_ptr;
391 input list<ComponentRef> crefs;
392 input UnorderedMap<Path, Function> funcMap;
393 output Boolean linear = true;
394 protected
395 Option<Expression> residual_opt = Equation.tryGetResidualExp(eqn_ptr);
396 Expression residual;
397 Differentiate.DifferentiationArguments diffArgs;
398 Expression derivative;
399 UnorderedSet<ComponentRef> names = UnorderedSet.fromList(list(ComponentRef.stripSubscriptsAll(c) for c in crefs), ComponentRef.hash, ComponentRef.isEqual);
400 list<ComponentRef> occurrences;
401 algorithm
402
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 if isSome(residual_opt) then
403 10 SOME(residual) := residual_opt;
404 // differentiate w.r.t. the crefs as they occur (e.g. x[$i1, :] in a for equation), the elements would give zero
405
6/6
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 19 times.
✓ Branch 7 taken 10 times.
✓ Branch 8 taken 10 times.
✓ Branch 9 taken 10 times.
29 occurrences := list(c for c guard(UnorderedSet.contains(ComponentRef.stripSubscriptsAll(c), names)) in UnorderedSet.toList(Expression.extractCrefs(residual)));
406
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if listEmpty(occurrences) then
407 occurrences := crefs;
408 end if;
409 try
410
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 9 times.
19 for cref in occurrences loop
411 10 diffArgs := Differentiate.DifferentiationArguments.simpleCref(cref, funcMap);
412 10 (derivative, diffArgs) := Differentiate.differentiateExpressionDump(residual, diffArgs, getInstanceName());
413
1/2
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
9 if Expression.fold(derivative, function containsNamedCref(names = names), false) then
414 linear := false;
415 end if;
416 end for;
417 else
418 // not everything can be differentiated, e.g. functions with function inputs
419 linear := false;
420 end try;
421 else
422 // no residual could be constructed at all (e.g. a record type such as a
423 // Medium's ThermodynamicState with no '+'/'-'/'0' operators, see
424 // Equation.getResidualExp) -- can't check, so assume the conservative
425 // (nonlinear) default instead of crashing the whole compilation.
426 linear := false;
427 end if;
428 end isLinearSlice;
429
430 function getModule
431 "Returns the module function that was chosen by the user."
432 output list<Module.tearingInterface> funcs;
433 protected
434 String flag = Flags.getConfigString(Flags.TEARING_METHOD);
435 function isNotGuruVar extends BVariable.checkVar;
436 input Boolean init;
437 algorithm
438 ✗ b := BVariable.hasTearingSelect(var_ptr, NFBackendExtension.TearingSelect.PREFER, intLt);
439 end isNotGuruVar;
440 algorithm
441 funcs := match flag
442 8 case "minimalTearing" then {function initialize(varFunc = BVariable.isDiscontinuous, eqnFunc = Equation.isDiscontinuous), minimal, finalize};
443 1512 case "cellier" then {function initialize(varFunc = BVariable.isDiscontinuous, eqnFunc = Equation.isDiscontinuous), minimal, cellier, finalize};
444 ✗ case "omcTearing" then {function initialize(varFunc = BVariable.isDiscontinuous, eqnFunc = Equation.isDiscontinuous), minimal, finalize}; // TODO set `minimal = false` when it's actually doing something
445 ✗ case "guruTearing" then {function initialize(varFunc = isNotGuruVar, eqnFunc = noFilterEqn), guru, finalize};
446 /* ... New tearing modules have to be added here */
447 else fail();
448 end match;
449 end getModule;
450
451 function getVariables
452 input Tearing tearing;
453 output list<Pointer<Variable>> variables;
454 algorithm
455
12/12
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 16 times.
✓ Branch 5 taken 41 times.
✓ Branch 6 taken 16 times.
✓ Branch 8 taken 41 times.
✓ Branch 9 taken 16 times.
✓ Branch 12 taken 57 times.
✓ Branch 13 taken 16 times.
✓ Branch 14 taken 57 times.
✓ Branch 15 taken 16 times.
188 variables := listAppend(var for var in list(Slice.getT(var) for var in tearing.iteration_vars) :: list(StrongComponent.getVariables(comp) for comp in tearing.innerEquations));
456 end getVariables;
457
458 function getResidualVars
459 input Tearing tearing;
460 output list<Pointer<Variable>> residuals = list(Equation.getResidualVar(Slice.getT(eqn)) for eqn in tearing.residual_eqns);
461 end getResidualVars;
462
463 function getIterationVars
464 input Tearing tearing;
465 output list<Pointer<Variable>> iterationVars = list(Slice.getT(var) for var in tearing.iteration_vars);
466 end getIterationVars;
467
468 function getResidualEqns
469 input Tearing tearing;
470 output list<Pointer<Equation>> residuals = list(Slice.getT(eqn) for eqn in tearing.residual_eqns);
471 end getResidualEqns;
472
473 function setResidualEqns
474 input output Tearing tearing;
475 input list<Slice<EquationPointer>> residuals;
476 algorithm
477 ✗ tearing.residual_eqns := residuals;
478 end setResidualEqns;
479
480 protected
481 // Traverser function
482 function tearingTraverser
483 input list<Partition.Partition> partitions;
484 input list<Module.tearingInterface> funcs;
485 output list<Partition.Partition> new_partitions = {};
486 input UnorderedMap<Path, Function> funcMap;
487 input Pointer<Integer> eq_index;
488 input Partition.Kind kind;
489 input Pointer<list<Pointer<Variable>>> new_vars "new whole arrays for partial resizable iteration variables";
490 protected
491 Pointer<list<Pointer<Variable>>> part_vars;
492 array<StrongComponent> strongComponents;
493 StrongComponent tmp;
494 Integer idx = 0;
495 Adjacency.Matrix full "full adjacency matrix containing solvability info";
496 Boolean resizable;
497 list<Module.tearingInterface> pre_funcs;
498 Module.tearingInterface fin;
499 algorithm
500
2/2
✓ Branch 0 taken 508 times.
✓ Branch 1 taken 383 times.
891 for part in partitions loop
501
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 508 times.
✓ Branch 2 taken 508 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 508 times.
✓ Branch 6 taken 508 times.
✗ Branch 7 not taken.
508 if isSome(part.strongComponents) and isSome(part.adjacencyMatrix) then
502 508 SOME(strongComponents) := part.strongComponents;
503 508 SOME(full) := part.adjacencyMatrix;
504 // with resizable arrays adjacent parts of the same loop are merged before
505 // they are finalized (the last function), see mergeResizableLoops
506 508 resizable := Flags.getConfigBool(Flags.RESIZABLE_ARRAYS);
507
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 483 times.
508 pre_funcs := if resizable then List.firstN(funcs, listLength(funcs) - 1) else funcs;
508
2/2
✓ Branch 0 taken 506 times.
✓ Branch 1 taken 2 times.
6523 for i in 1:arrayLength(strongComponents) loop
509 // each module has a list of functions that need to be applied
510 6015 tmp := strongComponents[i];
511
2/2
✓ Branch 0 taken 23038 times.
✓ Branch 1 taken 6015 times.
29053 for func in pre_funcs loop
512
2/2
✓ Branch 0 taken 6015 times.
✓ Branch 1 taken 17023 times.
23038 (tmp, full, idx) := func(tmp, full, funcMap, idx, part.unknowns, part.equations, eq_index, kind);
513 end for;
514 // only update if it changed
515
2/2
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 5919 times.
6015 if not referenceEq(tmp, strongComponents[i]) then
516 96 arrayUpdate(strongComponents, i, tmp);
517 end if;
518 end for;
519
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 483 times.
508 if resizable then
520 25 strongComponents := mergeResizableLoops(strongComponents);
521 25 fin := List.last(funcs);
522 25 part_vars := Pointer.create({});
523
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
197 for i in 1:arrayLength(strongComponents) loop
524 172 tmp := resizableIterationArrays(strongComponents[i], eq_index, part_vars);
525
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 172 times.
172 (tmp, full, idx) := fin(tmp, full, funcMap, idx, part.unknowns, part.equations, eq_index, kind);
526 172 arrayUpdate(strongComponents, i, tmp);
527 end for;
528
4/4
✓ Branch 0 taken 172 times.
✓ Branch 1 taken 25 times.
✓ Branch 3 taken 172 times.
✓ Branch 4 taken 25 times.
394 strongComponents := listArray(List.flatten(list(splitGenericSlices(c) for c in strongComponents)));
529 // the new whole arrays are unknowns of the partition (e.g. for the jacobian sparsity)
530 25 part.unknowns := VariablePointers.addList(Pointer.access(part_vars), part.unknowns);
531 25 Pointer.update(new_vars, listAppend(Pointer.access(part_vars), Pointer.access(new_vars)));
532 end if;
533 508 part.strongComponents := SOME(strongComponents);
534 part.adjacencyMatrix := SOME(full);
535 end if;
536 new_partitions := part :: new_partitions;
537 end for;
538 383 new_partitions := listReverse(new_partitions);
539 end tearingTraverser;
540
541 function resizableIterationArrays
542 "Partial slices of resizable arrays as iteration variables are only known at
543 the analysis sizes. A slice that consists of boxes of elements becomes new
544 whole resizable arrays, the elements of the slice are assigned from them first."
545 input output StrongComponent comp;
546 input Pointer<Integer> eq_index;
547 input Pointer<list<Pointer<Variable>>> new_vars;
548 protected
549 list<Slice<VariablePointer>> vars = {}, new_slices;
550 list<StrongComponent> copies = {}, new_copies;
551 algorithm
552 comp := match comp
553 local
554 Tearing strict;
555 case StrongComponent.ALGEBRAIC_LOOP(strict = strict as TEARING_SET()) algorithm
556
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
28 for slice in strict.iteration_vars loop
557 14 (new_slices, new_copies) := resizableIterationArray(wholeVarSlice(slice), eq_index, new_vars);
558 14 vars := List.append_reverse(new_slices, vars);
559 14 copies := List.append_reverse(new_copies, copies);
560 end for;
561
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
14 if not listEmpty(copies) then
562 4 strict.iteration_vars := listReverse(vars);
563 4 strict.innerEquations := listArray(listAppend(listReverse(copies), arrayList(strict.innerEquations)));
564 4 strict.jac := NONE();
565 4 comp.strict := strict;
566 end if;
567 then comp;
568 else comp;
569 end match;
570 end resizableIterationArrays;
571
572 function resizableIterationArray
573 "x[2:N] as iteration variable becomes $RZ[1:N-1] with the inner equation
574 for i in 2:N loop x[i] = $RZ[i - 1]; end for; a slice that is no box is
575 split into boxes first"
576 input Slice<VariablePointer> slice;
577 output list<Slice<VariablePointer>> slices = {slice};
578 output list<StrongComponent> copies = {};
579 input Pointer<Integer> eq_index;
580 input Pointer<list<Pointer<Variable>>> new_vars;
581 protected
582 Variable var = Pointer.access(Slice.getT(slice));
583 list<Dimension> dims;
584 list<ComponentRef> iters;
585 list<Expression> ranges;
586 Iterator full_iter;
587 list<list<Integer>> parts;
588 list<Option<Iterator>> o_iters;
589 StrongComponent copy;
590 Slice<VariablePointer> new_slice;
591 algorithm
592 14 dims := Type.arrayDims(var.ty);
593
5/6
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
14 if listEmpty(slice.indices) or not List.any(dims, Dimension.isResizable) or not Type.isReal(Type.arrayElementType(var.ty)) then
594 10 return;
595 end if;
596
597 // the slice as boxes over the dimensions
598
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 iters := list(BVariable.getVarName(BackendDAE.lowerIterator(ComponentRef.makeIterator(InstNode.newUniqueIterator(), Type.INTEGER()))) for d in dims);
599
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 4 times.
16 ranges := list(Expression.RANGE(Type.ARRAY(Type.INTEGER(), {d}), Expression.INTEGER(1), NONE(), Dimension.sizeExp(d)) for d in dims);
600
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 full_iter := Iterator.fromFrames(List.zip3(iters, ranges, list(NONE() for d in dims)));
601 4 o_iters := {Resizable.restrictIterator(full_iter, slice.indices)};
602
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
4 if isNone(listHead(o_iters)) then
603 ✗ parts := Resizable.boxes(slice.indices, Iterator.sizes(full_iter, true));
604 ✗ o_iters := list(Resizable.restrictIterator(full_iter, part) for part in parts);
605 end if;
606
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 if listEmpty(o_iters) or List.any(o_iters, isNone) then
607 ✗ return;
608 end if;
609
610 slices := {};
611
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for o_iter in o_iters loop
612 4 (new_slice, copy) := resizableIterationBox(Slice.getT(slice), iters, Util.getOption(o_iter), eq_index, new_vars);
613 slices := new_slice :: slices;
614 4 copies := copy :: copies;
615 end for;
616 4 slices := listReverse(slices);
617 4 copies := listReverse(copies);
618 end resizableIterationArray;
619
620 function resizableIterationBox
621 "the new whole array for a box of a variable and the assignment of its elements"
622 input Pointer<Variable> var_ptr;
623 input list<ComponentRef> iters "the iterators of the dimensions";
624 input Iterator iter "the iterators restricted to the box";
625 output Slice<VariablePointer> slice;
626 output StrongComponent copy;
627 input Pointer<Integer> eq_index;
628 input Pointer<list<Pointer<Variable>>> new_vars;
629 protected
630 Variable var = Pointer.access(var_ptr);
631 list<ComponentRef> names;
632 list<Expression> ranges;
633 Pointer<Variable> aux_ptr;
634 ComponentRef aux_cref, elem_cref;
635 Expression lhs, rhs, start;
636 list<Subscript> aux_subs = {};
637 Pointer<Equation> eqn;
638 UnorderedMap<ComponentRef, EvalOrder> order;
639 algorithm
640 4 (names, ranges, _) := Iterator.getFrames(iter);
641
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 4 times.
10 (aux_ptr, aux_cref) := BVariable.makeAuxVar("$RZ", Pointer.access(eq_index), Type.ARRAY(Type.arrayElementType(var.ty),
642 list(Type.nthDimension(Expression.typeOf(r), 1) for r in ranges)), false);
643 8 Pointer.update(new_vars, aux_ptr :: Pointer.access(new_vars));
644
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 4 times.
10 for tpl in List.zip(names, ranges) loop
645
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 Expression.RANGE(start = start) := Util.tuple22(tpl);
646 12 aux_subs := Subscript.INDEX(SimplifyExp.simplify(Expression.MULTARY({Expression.fromCref(Util.tuple21(tpl)), Expression.INTEGER(1)},
647 {start}, Operator.makeAdd(Type.INTEGER())))) :: aux_subs;
648 end for;
649 4 aux_subs := listReverse(aux_subs);
650
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 4 times.
10 elem_cref := ComponentRef.mergeSubscripts(list(Subscript.INDEX(Expression.fromCref(i)) for i in iters), var.name, true, true);
651 4 lhs := Expression.fromCref(elem_cref);
652 4 rhs := Expression.fromCref(ComponentRef.mergeSubscripts(aux_subs, aux_cref, true, true));
653 4 eqn := Equation.makeAssignment(lhs, rhs, eq_index, NBEquation.SIMULATION_STR, iter, EquationAttributes.default(EquationKind.CONTINUOUS, false));
654 4 order := UnorderedMap.new<EvalOrder>(ComponentRef.hash, ComponentRef.isEqual);
655
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 for n in names loop
656 6 UnorderedMap.add(n, EvalOrder.INDEPENDENT, order);
657 end for;
658 4 copy := StrongComponent.RESIZABLE_COMPONENT(elem_cref, Slice.SLICE(var_ptr, {}), Slice.SLICE(eqn, {}), order, NBSolve.Status.EXPLICIT);
659 4 slice := Slice.SLICE(aux_ptr, {});
660 end resizableIterationBox;
661
662 function mergeResizableLoops
663 "Adjacent algebraic loops that are parts of the same variables and equations
664 are merged if together they are whole: with resizable arrays the parts are
665 only known for the analysis sizes, the whole loop is valid for every size.
666 Solving adjacent loops together is always correct."
667 input array<StrongComponent> comps;
668 output array<StrongComponent> outComps;
669 protected
670 list<StrongComponent> acc = {};
671 Option<StrongComponent> merged;
672 StrongComponent m;
673 algorithm
674
2/2
✓ Branch 1 taken 172 times.
✓ Branch 2 taken 25 times.
197 for c in comps loop
675
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 25 times.
172 if not listEmpty(acc) then
676 147 merged := mergeLoops(listHead(acc), c);
677
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 147 times.
147 if isSome(merged) then
678 ✗ SOME(m) := merged;
679 ✗ acc := m :: listRest(acc);
680 ✗ continue;
681 end if;
682 end if;
683 acc := c :: acc;
684 end for;
685 25 outComps := listArray(listReverse(acc));
686 end mergeResizableLoops;
687
688 function mergeLoops
689 input StrongComponent comp1;
690 input StrongComponent comp2;
691 output Option<StrongComponent> merged = NONE();
692 protected
693 Option<list<Slice<VariablePointer>>> ovars;
694 Option<list<Slice<EquationPointer>>> oeqns;
695 list<Slice<VariablePointer>> vars;
696 list<Slice<EquationPointer>> eqns;
697 algorithm
698 merged := match (comp1, comp2)
699 local
700 StrongComponent c1, c2;
701 Tearing t1, t2;
702 case (c1 as StrongComponent.ALGEBRAIC_LOOP(strict = t1 as TEARING_SET(), casual = NONE()),
703 c2 as StrongComponent.ALGEBRAIC_LOOP(strict = t2 as TEARING_SET(), casual = NONE()))
704 algorithm
705 4 ovars := mergeSliceLists(t1.iteration_vars, t2.iteration_vars, function sliceName(name = varSliceName));
706 4 oeqns := mergeSliceLists(t1.residual_eqns, t2.residual_eqns, function sliceName(name = eqnSliceName));
707
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
4 if isSome(ovars) and isSome(oeqns) then
708 ✗ SOME(vars) := ovars;
709 ✗ SOME(eqns) := oeqns;
710 ✗ vars := list(wholeVarSlice(v) for v in vars);
711 ✗ eqns := list(wholeEqnSlice(e) for e in eqns);
712 ✗ if List.all(list(listEmpty(v.indices) for v in vars), Util.id) and
713 List.all(list(listEmpty(e.indices) for e in eqns), Util.id) then
714 ✗ t1.iteration_vars := vars;
715 ✗ t1.residual_eqns := eqns;
716 ✗ t1.innerEquations := listArray(listAppend(arrayList(t1.innerEquations), arrayList(t2.innerEquations)));
717 ✗ t1.jac := NONE();
718 ✗ c1.strict := t1;
719 ✗ c1.linear := c1.linear and c2.linear;
720 ✗ c1.mixed := c1.mixed or c2.mixed;
721 ✗ c1.homotopy := c1.homotopy or c2.homotopy;
722 merged := SOME(c1);
723 end if;
724 end if;
725 then merged;
726 else NONE();
727 end match;
728 end mergeLoops;
729
730 function varSliceName
731 input Slice<VariablePointer> slice;
732 output String name = ComponentRef.toString(BVariable.getVarName(Slice.getT(slice)));
733 end varSliceName;
734
735 function eqnSliceName
736 input Slice<EquationPointer> slice;
737 output String name = ComponentRef.toString(Equation.getEqnName(Slice.getT(slice)));
738 end eqnSliceName;
739
740 function sliceName<T>
741 input Slice<T> slice;
742 input NameFunc name;
743 output String str = name(slice);
744 partial function NameFunc
745 input Slice<T> slice;
746 output String str;
747 end NameFunc;
748 end sliceName;
749
750 function mergeSliceLists<T>
751 "the slices of the same objects (by name) merged, NONE if the lists do not
752 contain the same objects"
753 input list<Slice<T>> l1;
754 input list<Slice<T>> l2;
755 input NameFunc name;
756 output Option<list<Slice<T>>> merged = NONE();
757 partial function NameFunc
758 input Slice<T> slice;
759 output String str;
760 end NameFunc;
761 protected
762 list<Slice<T>> res = {};
763 Slice<T> s2;
764 array<Slice<T>> arr2 = listArray(l2);
765 Option<Integer> opos;
766 Integer pos;
767 UnorderedMap<String, Integer> by_name "name -> position in l2";
768 UnorderedSet<Integer> indices;
769 algorithm
770
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if listLength(l1) <> listLength(l2) then
771 ✗ return;
772 end if;
773 8 by_name := UnorderedMap.new<Integer>(stringHashDjb2, stringEq);
774
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
16 for i in 1:arrayLength(arr2) loop
775
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 UnorderedMap.add(name(arr2[i]), i, by_name);
776 end for;
777
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
12 for s1 in l1 loop
778
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 opos := UnorderedMap.get(name(s1), by_name);
779
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
8 if isNone(opos) then
780 4 return;
781 end if;
782 4 SOME(pos) := opos;
783 4 s2 := arr2[pos];
784
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if listEmpty(s1.indices) or listEmpty(s2.indices) then
785 ✗ res := Slice.SLICE(Slice.getT(s1), {}) :: res;
786 else
787 4 indices := UnorderedSet.fromList(s1.indices, Util.id, intEq);
788
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for i in s2.indices loop
789 4 UnorderedSet.add(i, indices);
790 end for;
791 4 res := Slice.SLICE(Slice.getT(s1), List.sort(UnorderedSet.toList(indices), intGt)) :: res;
792 end if;
793 end for;
794 4 merged := SOME(listReverse(res));
795 end mergeSliceLists;
796
797 function noFilterVar extends BVariable.checkVar;
798 input Boolean init;
799 algorithm
800 b := true;
801 end noFilterVar;
802
803 function noFilterEqn extends BEquation.checkEqn;
804 algorithm
805 b := true;
806 end noFilterEqn;
807
808 function tolerantSubMap
809 "Like UnorderedMap.subMap, but silently skips keys that don't exist in
810 map instead of crashing (UnorderedMap.subMap uses getSafe)."
811 input UnorderedMap<ComponentRef, Integer> map;
812 input list<ComponentRef> lst;
813 output UnorderedMap<ComponentRef, Integer> sub_map =
814 UnorderedMap.subMap(map, list(k for k guard UnorderedMap.contains(k, map) in lst));
815 end tolerantSubMap;
816
817 function initialize
818 extends Module.tearingInterface;
819 input checkVarInit varFunc = noFilterVar;
820 input BEquation.checkEqn eqnFunc = noFilterEqn;
821 // varFunc/eqnFunc kept for API compatibility with getModule()'s partial applications,
822 // but no longer consulted below (see comment further down).
823 partial function checkVarInit extends BVariable.checkVar;
824 input Boolean init;
825 end checkVarInit;
826 protected
827 Tearing strict;
828 list<ComponentRef> all_vars_lst, all_eqns_lst;
829 UnorderedSet<ComponentRef> vars_set "all loop vars, used by refine to detect self-referential (nonlinear) dependencies";
830 UnorderedMap<ComponentRef, Integer> v_all, e_all "unfiltered loop vars/equations map";
831 algorithm
832 (comp, full, index) := match comp
833 case StrongComponent.ALGEBRAIC_LOOP(strict = strict) algorithm
834 96 index := index + 1;
835 96 comp.idx := index;
836
837 // refine and checkLinearity both need the loop's full, unfiltered var/eqn set --
838 // varFunc/eqnFunc's filtered one is often EMPTY for a purely continuous loop,
839 // which silently misclassified such loops as linear (see #16463). Tolerant
840 // lookup: not every name here is necessarily registered in variables.map/
841 // equations.map yet.
842
4/4
✓ Branch 0 taken 1526 times.
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 1526 times.
✓ Branch 3 taken 96 times.
1622 all_vars_lst := list(BVariable.getVarName(Slice.getT(var)) for var in strict.iteration_vars);
843
4/4
✓ Branch 0 taken 1659 times.
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 1659 times.
✓ Branch 3 taken 96 times.
1755 all_eqns_lst := list(Equation.getEqnName(Slice.getT(eqn)) for eqn in strict.residual_eqns);
844 96 vars_set := UnorderedSet.fromList(all_vars_lst, ComponentRef.hash, ComponentRef.isEqual);
845 96 v_all := tolerantSubMap(variables.map, all_vars_lst);
846 96 e_all := tolerantSubMap(equations.map, all_eqns_lst);
847
848 // refine the adjacency matrix by updating solvability information
849 96 full := Adjacency.Matrix.refine(full, funcMap, v_all, e_all, variables, equations, vars_set, Partition.kindIsInitial(kind));
850
851
2/2
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 36 times.
156 comp.linear := checkLinearity(full, v_all, e_all);
852 then (comp, full, index);
853 else (comp, full, index);
854 end match;
855 end initialize;
856
857 function isPartialVarSlice
858 input Slice<VariablePointer> var;
859 output Boolean b = not listEmpty(var.indices) and BVariable.size(Slice.getT(var)) > listLength(var.indices);
860 end isPartialVarSlice;
861
862 function isPartialArraySlice
863 input Slice<EquationPointer> eqn;
864 output Boolean b = not listEmpty(eqn.indices) and (Equation.isArrayEquation(Slice.getT(eqn)) or Equation.isSingleBodyFor(Pointer.access(Slice.getT(eqn))))
865 and Equation.size(Slice.getT(eqn)) > listLength(eqn.indices);
866 end isPartialArraySlice;
867
868 function isArrayBodyFor
869 "a for equation with an array body, its residual rows are split (codegen writes them per iteration)"
870 input Slice<EquationPointer> eqn;
871 output Boolean b;
872 algorithm
873 b := match Pointer.access(Slice.getT(eqn))
874 local
875 Equation body;
876 35 case Equation.FOR_EQUATION(body = {body}) then Type.isArray(Equation.getType(body));
877 else false;
878 end match;
879 end isArrayBodyFor;
880
881 function finalize extends Module.tearingInterface;
882 protected
883 Tearing strict;
884 Boolean partial_vars;
885 list<list<Slice<EquationPointer>>> acc;
886 UnorderedSet<VariablePointer> dummy_set = UnorderedSet.new(BVariable.hash, BVariable.equalName);
887 algorithm
888 comp := match comp
889 case StrongComponent.ALGEBRAIC_LOOP(strict = strict) algorithm
890 // slices with all elements are whole slices: with resizable arrays the
891 // elements are only known for the analysis sizes, a whole slice stays
892 // valid for every size
893
4/4
✓ Branch 0 taken 1188 times.
✓ Branch 1 taken 106 times.
✓ Branch 2 taken 1188 times.
✓ Branch 3 taken 106 times.
1400 strict.iteration_vars := list(wholeVarSlice(v) for v in strict.iteration_vars);
894
4/4
✓ Branch 0 taken 1231 times.
✓ Branch 1 taken 106 times.
✓ Branch 2 taken 1231 times.
✓ Branch 3 taken 106 times.
1443 strict.residual_eqns := list(wholeEqnSlice(e) for e in strict.residual_eqns);
895
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 92 times.
106 if Flags.getConfigBool(Flags.RESIZABLE_ARRAYS) then
896
4/4
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 14 times.
46 strict.residual_eqns := List.flatten(list(restrictedForSlice(e, eq_index) for e in strict.residual_eqns));
897 end if;
898
899 // inline potential records
900
4/4
✓ Branch 0 taken 1231 times.
✓ Branch 1 taken 106 times.
✓ Branch 2 taken 1231 times.
✓ Branch 3 taken 106 times.
1337 acc := list(Inline.inlineRecordSliceEquation(eqn, variables, dummy_set, eq_index, true) for eqn in strict.residual_eqns);
901
902 // create residual equations, a part of an array equation needs residual variables for its rows only.
903 // for equations are also split into rows if an iteration variable is only partially part of the loop,
904 // otherwise the jacobian can not differentiate them w.r.t. single elements of that variable.
905 106 partial_vars := List.any(strict.iteration_vars, isPartialVarSlice);
906
15/16
✓ Branch 1 taken 1237 times.
✓ Branch 2 taken 106 times.
✓ Branch 3 taken 1237 times.
✓ Branch 4 taken 106 times.
✓ Branch 6 taken 1235 times.
✓ Branch 7 taken 2 times.
✓ Branch 9 taken 1235 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 91 times.
✓ Branch 12 taken 1144 times.
✓ Branch 16 taken 13 times.
✓ Branch 17 taken 78 times.
✓ Branch 20 taken 1287 times.
✓ Branch 21 taken 106 times.
✓ Branch 22 taken 1287 times.
✓ Branch 23 taken 106 times.
2736 strict.residual_eqns := list(Slice.apply(eqn, function Equation.createResidual(residualCref_opt = NONE(), new = true, allowFail = false))
907 for eqn in List.flatten(list(if isPartialArraySlice(eqn) or isArrayBodyFor(eqn) or (partial_vars and Equation.isSingleBodyFor(Pointer.access(Slice.getT(eqn))))
908 then scalarSlices(eqn) else {eqn} for eqn in List.flatten(acc))));
909 106 comp.strict := strict;
910
911
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 106 times.
106 if Flags.isSet(Flags.TEARING_DUMP) then
912 ✗ print(StringUtil.headline_2("[" + Partition.Partition.kindToString(kind) + "] Tearing Result " + intString(comp.idx)) + "\n" + StrongComponent.toString(comp) + "\n");
913 end if;
914 then comp;
915 else comp;
916 end match;
917 end finalize;
918
919 function numUnique
920 "the number of different indices"
921 input list<Integer> indices;
922 output Integer n = UnorderedSet.size(UnorderedSet.fromList(indices, Util.id, intEq));
923 end numUnique;
924
925 function wholeVarSlice
926 "a slice of all elements of a variable (at the resized sizes) as whole slice"
927 input output Slice<VariablePointer> slice;
928 algorithm
929
4/4
✓ Branch 0 taken 1152 times.
✓ Branch 1 taken 50 times.
✓ Branch 5 taken 46 times.
✓ Branch 6 taken 4 times.
1202 if not listEmpty(slice.indices) and
930 numUnique(slice.indices) == BVariable.size(Slice.getT(slice), true) then
931 4 slice := Slice.SLICE(Slice.getT(slice), {});
932 end if;
933 end wholeVarSlice;
934
935 function restrictedForSlice
936 "a slice of a for-equation with a scalar body over a resizable range that
937 consists of boxes of iterations as whole for-equations over the symbolic
938 sub-ranges"
939 input Slice<EquationPointer> slice;
940 input Pointer<Integer> eq_index;
941 output list<Slice<EquationPointer>> slices = {slice};
942 protected
943 Equation eqn;
944 list<Option<Iterator>> iters;
945 Pointer<Equation> eqn_ptr;
946 algorithm
947 18 eqn := Pointer.access(Slice.getT(slice));
948
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
18 if listEmpty(slice.indices) or not Equation.isForEquation(Slice.getT(slice)) then
949 16 return;
950 end if;
951 () := match eqn
952 case Equation.FOR_EQUATION() guard Iterator.isResizable(eqn.iter) and
953 Equation.size(Slice.getT(slice), true) == Iterator.size(eqn.iter, true) algorithm
954 2 iters := restrictedIterators(eqn.iter, slice.indices);
955
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if not listEmpty(iters) then
956 slices := {};
957
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 for iter in iters loop
958 2 eqn.iter := Util.getOption(iter);
959 2 eqn.size := Iterator.size(eqn.iter);
960 // a new residual variable with the restricted sizes
961 2 eqn_ptr := Pointer.create(eqn);
962 2 Equation.createName(eqn_ptr, eq_index, NBEquation.SIMULATION_STR);
963 2 slices := Slice.SLICE(eqn_ptr, {}) :: slices;
964 end for;
965 2 slices := listReverse(slices);
966 end if;
967 then ();
968 else ();
969 end match;
970 end restrictedForSlice;
971
972 function restrictedIterators
973 "the iterator restricted to the boxes of the iterations, empty if not possible"
974 input Iterator iter;
975 input list<Integer> indices;
976 output list<Option<Iterator>> iters;
977 algorithm
978 2 iters := {Resizable.restrictIterator(iter, indices)};
979
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
2 if isNone(listHead(iters)) then
980 ✗ iters := list(Resizable.restrictIterator(iter, part) for part in Resizable.boxes(indices, Iterator.sizes(iter, true)));
981 end if;
982
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if List.any(iters, isNone) then
983 iters := {};
984 end if;
985 end restrictedIterators;
986
987 function splitGenericSlices
988 "generic or sliced components of resizable for-equations whose slices are no
989 boxes are split into boxes (also inside algebraic loops)"
990 input StrongComponent comp;
991 output list<StrongComponent> comps;
992 algorithm
993 comps := match comp
994 local
995 Tearing strict;
996 Equation eqn;
997 list<Option<Iterator>> iters;
998 case StrongComponent.GENERIC_COMPONENT() guard not listEmpty(comp.eqn.indices) and Equation.isForEquation(Slice.getT(comp.eqn)) algorithm
999 ✗ eqn := Pointer.access(Slice.getT(comp.eqn));
1000 ✗ if Iterator.isResizable(Equation.getForIterator(eqn)) and isNone(Resizable.restrictIterator(Equation.getForIterator(eqn), comp.eqn.indices))
1001 and Equation.size(Slice.getT(comp.eqn), true) == Iterator.size(Equation.getForIterator(eqn), true) then
1002 ✗ iters := restrictedIterators(Equation.getForIterator(eqn), comp.eqn.indices);
1003 else
1004 iters := {};
1005 end if;
1006 ✗ then if listEmpty(iters) then {comp} else list(StrongComponent.GENERIC_COMPONENT(comp.var_cref, comp.var, Slice.SLICE(Slice.getT(comp.eqn), part))
1007 for part in Resizable.boxes(comp.eqn.indices, Iterator.sizes(Equation.getForIterator(eqn), true)));
1008 // not yet solved slices (e.g. inner equations of algebraic loops)
1009 case StrongComponent.SLICED_COMPONENT() guard not listEmpty(comp.eqn.indices) and Equation.isForEquation(Slice.getT(comp.eqn)) algorithm
1010 38 eqn := Pointer.access(Slice.getT(comp.eqn));
1011
5/10
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 38 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 14 taken 16 times.
✓ Branch 15 taken 22 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 16 times.
38 if Iterator.isResizable(Equation.getForIterator(eqn)) and isNone(Resizable.restrictIterator(Equation.getForIterator(eqn), comp.eqn.indices))
1012 and Equation.size(Slice.getT(comp.eqn), true) == Iterator.size(Equation.getForIterator(eqn), true) then
1013 ✗ iters := restrictedIterators(Equation.getForIterator(eqn), comp.eqn.indices);
1014 else
1015 iters := {};
1016 end if;
1017
1/6
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
38 then if listEmpty(iters) then {comp} else list(StrongComponent.SLICED_COMPONENT(comp.var_cref, comp.var, Slice.SLICE(Slice.getT(comp.eqn), part), comp.status)
1018 for part in Resizable.boxes(comp.eqn.indices, Iterator.sizes(Equation.getForIterator(eqn), true)));
1019 case StrongComponent.ALGEBRAIC_LOOP(strict = strict as TEARING_SET()) algorithm
1020
4/4
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 14 times.
✓ Branch 3 taken 66 times.
✓ Branch 4 taken 14 times.
174 strict.innerEquations := listArray(List.flatten(list(splitGenericSlices(c) for c in strict.innerEquations)));
1021 14 comp.strict := strict;
1022 then {comp};
1023 else {comp};
1024 end match;
1025 end splitGenericSlices;
1026
1027 function wholeEqnSlice
1028 "a slice of all elements of an equation (at the resized sizes) as whole slice"
1029 input output Slice<EquationPointer> slice;
1030 algorithm
1031
4/4
✓ Branch 0 taken 1216 times.
✓ Branch 1 taken 15 times.
✓ Branch 5 taken 13 times.
✓ Branch 6 taken 2 times.
1231 if not listEmpty(slice.indices) and
1032 numUnique(slice.indices) == Equation.size(Slice.getT(slice), true) then
1033 2 slice := Slice.SLICE(Slice.getT(slice), {});
1034 end if;
1035 end wholeEqnSlice;
1036
1037 function minimal extends Module.tearingInterface;
1038 // only extracts discrete variables to be solved as inner equations
1039 protected
1040 Tearing strict, innerStrict;
1041 list<Pointer<Variable>> vars_lst, cont_vars, disc_vars, implied_vars, alg_implied;
1042 list<Pointer<Equation>> eqns_lst, cont_eqns, disc_eqns, alg_eqns;
1043 Integer num_vars, num_eqns;
1044 list<Slice<VariablePointer>> iteration_vars = {};
1045 Adjacency.Matrix adj, sub;
1046 Matching matching;
1047 list<StrongComponent> inner_comps;
1048 VariablePointers disc_variables;
1049 EquationPointers disc_equations;
1050 UnorderedSet<ComponentRef> matched_set = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
1051 algorithm
1052 comp := match comp
1053 case StrongComponent.ALGEBRAIC_LOOP(strict = strict) algorithm
1054 // split equations and variables for discretes and continuous
1055
4/4
✓ Branch 0 taken 1526 times.
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 1526 times.
✓ Branch 3 taken 96 times.
1622 vars_lst := list(Slice.getT(var) for var in strict.iteration_vars);
1056
4/4
✓ Branch 0 taken 1659 times.
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 1659 times.
✓ Branch 3 taken 96 times.
1755 eqns_lst := list(Slice.getT(eqn) for eqn in strict.residual_eqns);
1057 96 (cont_vars, disc_vars) := filterDiscreteVariables(vars_lst, Partition.kindIsInitial(kind));
1058 96 (cont_eqns, disc_eqns) := List.splitOnTrue(eqns_lst, Equation.isContinousRecordAware);
1059 // extract continuous algorithm equations; they have implied inner variables and cannot be residuals
1060 96 (alg_eqns, cont_eqns) := List.splitOnTrue(cont_eqns, Equation.isAlgorithm);
1061
1062 // get the implied vars by algorithms and tuples (from both alg_eqns and disc_eqns)
1063
4/4
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 96 times.
103 implied_vars := List.flatten(list(getImpliedInnerVars(eqn) for eqn in listAppend(alg_eqns, disc_eqns)));
1064 96 disc_vars := UnorderedSet.unique_list(listAppend(disc_vars, implied_vars), BVariable.hash, BVariable.equalName);
1065 96 cont_vars := UnorderedSet.difference_list(cont_vars, implied_vars, BVariable.hash, BVariable.equalName);
1066
1067
4/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 96 times.
106 num_vars := sum(BVariable.size(var) for var in disc_vars);
1068
8/8
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 96 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 96 times.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 96 times.
103 num_eqns := sum(Equation.size(eqn) for eqn in disc_eqns) + sum(Equation.size(eqn) for eqn in alg_eqns);
1069
1070 // do nothing if there are no discrete or algorithm equations
1071
4/4
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 91 times.
96 if not (listEmpty(disc_eqns) and listEmpty(alg_eqns)) then
1072 5 comp.mixed := true;
1073 inner_comps := {};
1074
1075 // algorithm equations: create MULTI_COMPONENTs directly with ALL outputs as inner variables
1076 // (they cannot be residuals and have multiple outputs, so standard 1:1 matching is wrong)
1077
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 for alg_eqn in alg_eqns loop
1078 3 alg_implied := getImpliedInnerVars(alg_eqn);
1079
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 for var in alg_implied loop
1080 6 UnorderedSet.add(BVariable.getVarName(var), matched_set);
1081 end for;
1082
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 3 times.
9 inner_comps := StrongComponent.MULTI_COMPONENT(
1083 list(Slice.SLICE(var, {}) for var in alg_implied),
1084 Slice.SLICE(alg_eqn, {}),
1085 NBSolve.Status.UNPROCESSED
1086 ) :: inner_comps;
1087 end for;
1088
1089
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if not listEmpty(disc_eqns) then
1090 // local system of the discrete variables and equations, in the order of the full system
1091
5/6
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 2 times.
6 disc_vars := sortByIndex(list(var for var guard(not UnorderedSet.contains(BVariable.getVarName(var), matched_set)) in disc_vars), BVariable.getVarName, variables.map);
1092 2 disc_eqns := sortByIndex(disc_eqns, Equation.getEqnName, equations.map);
1093 2 disc_variables := VariablePointers.fromList(disc_vars);
1094 2 disc_equations := EquationPointers.fromList(disc_eqns);
1095
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 2 times.
6 sub := Adjacency.Matrix.subFull(full, list(UnorderedMap.getSafe(Equation.getEqnName(eqn), equations.map, sourceInfo()) for eqn in disc_eqns), disc_equations, disc_variables);
1096
1097 // match the discretes to create inner components
1098 2 adj := Adjacency.Matrix.fullToFinal(sub, disc_variables.map, disc_equations.map, disc_equations, NBAdjacency.MatrixStrictness.MATCHING);
1099 2 matching := Matching.regular(NBMatching.EMPTY_MATCHING, adj, true, true);
1100
1101 // build the matched variables set
1102
2/2
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 2 times.
6 for var in Matching.getMatchedVars(matching, Adjacency.Matrix.getMappingOpt(adj), disc_variables.map, disc_variables) loop
1103 4 UnorderedSet.add(BVariable.getVarName(var), matched_set);
1104 end for;
1105
1106 // upgrade adjacency matrix and sort the system creating inner equation components
1107 2 adj := Adjacency.Matrix.upgrade(adj, sub, disc_variables.map, disc_equations.map, disc_equations, NBAdjacency.MatrixStrictness.SORTING);
1108 2 inner_comps := listAppend(Sorting.tarjan(adj, matching, disc_variables, disc_equations), inner_comps);
1109 end if;
1110
1111 // only take variables that are not in the matched set
1112
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5 times.
23 for var in strict.iteration_vars loop
1113
2/2
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 10 times.
18 if not UnorderedSet.contains(BVariable.getVarName(Slice.getT(var)), matched_set) then
1114 iteration_vars := var :: iteration_vars;
1115 end if;
1116 end for;
1117
1118 5 strict.innerEquations := listArray(inner_comps);
1119
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 5 times.
18 strict.residual_eqns := list(Slice.SLICE(eqn, {}) for eqn in cont_eqns);
1120 5 strict.iteration_vars := listReverse(iteration_vars);
1121 5 comp.strict := strict;
1122 end if;
1123 then comp;
1124 else comp;
1125 end match;
1126 end minimal;
1127
1128 function sortByIndex<T>
1129 "sorts the elements by their index in the map"
1130 input list<T> lst;
1131 input getName func;
1132 input UnorderedMap<ComponentRef, Integer> map;
1133 output list<T> sorted;
1134 partial function getName
1135 input T t;
1136 output ComponentRef name;
1137 end getName;
1138 protected
1139 list<tuple<Integer, T>> indexed = list((UnorderedMap.getSafe(func(t), map, sourceInfo()), t) for t in lst);
1140 algorithm
1141 4 indexed := List.sort(indexed, function Util.compareTupleIntGt());
1142
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 4 times.
12 sorted := list(Util.tuple22(tpl) for tpl in indexed);
1143 end sortByIndex;
1144
1145 function guru extends Module.tearingInterface;
1146 protected
1147 list<StrongComponent> inner_comps = {};
1148 list<EqnSlice> residuals = {};
1149 Tearing strict;
1150 Integer nEqn;
1151 list<VarSlice> inner_vars, guru_vars, failed_vars;
1152 UnorderedMap<ComponentRef, VarSlice> unsolved_inner_vars;
1153 UnorderedMap<ComponentRef, EqnSlice> unsolved_equations;
1154 Option<ComponentRef> solve_opt;
1155 ComponentRef solve_cref;
1156 VarSlice solve_var;
1157 EqnSlice solve_eqn;
1158 Boolean success, var_assigned;
1159 ComponentRef stripped;
1160 constant Boolean staticAsContinuous = Partition.kindIsInitial(kind);
1161 algorithm
1162 comp := match (comp, full)
1163 case (StrongComponent.ALGEBRAIC_LOOP(strict = strict), Adjacency.FULL()) algorithm
1164 ✗ nEqn := arrayLength(full.equation_names);
1165
1166 // split variables to inner variables and guru iteration vars
1167 ✗ (inner_vars, guru_vars) := List.splitOnTrue(strict.iteration_vars,
1168 function Slice.check(func = function BVariable.hasTearingSelect(compareTS = NFBackendExtension.TearingSelect.PREFER, func = intLt)));
1169
1170 ✗ if listEmpty(guru_vars) then
1171 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. No guru variables provided for strong component:\n"
1172 + StrongComponent.toString(comp)});
1173 ✗ fail();
1174 else
1175 ✗ failed_vars := list(var for var guard(Slice.check(var, function NBVariable.isDiscontinuous(staticAsContinuous = staticAsContinuous))) in guru_vars);
1176 ✗ if not listEmpty(failed_vars) then
1177 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Following variables cannot be chosen as iteration variables because they are discontinuous:\n"
1178 + List.toString(failed_vars, function Slice.toString(func = BVariable.pointerToString, maxLength = 10), List.Style.NEWLINE_TAB)});
1179 ✗ fail();
1180 end if;
1181
1182 // collect the (yet) unsolved inner vars as a map of their name to their variable slice
1183 // at the end of the algorithm this set has to be empty
1184 ✗ unsolved_inner_vars := UnorderedMap.new<VarSlice>(ComponentRef.hash, ComponentRef.isEqual);
1185 ✗ for var in inner_vars loop
1186 ✗ UnorderedMap.add(BVariable.getVarName(Slice.getT(var)), var, unsolved_inner_vars);
1187 end for;
1188
1189 // collect the (yet) unsolved equations. all remaining at the end will be residual
1190 ✗ unsolved_equations := UnorderedMap.new<EqnSlice>(ComponentRef.hash, ComponentRef.isEqual);
1191 ✗ for eqn in strict.residual_eqns loop
1192 ✗ UnorderedMap.add(Equation.getEqnName(Slice.getT(eqn)), eqn, unsolved_equations);
1193 end for;
1194
1195 // main routine finding the inner variable and equation pairs
1196 ✗ while(not UnorderedMap.isEmpty(unsolved_inner_vars)) loop
1197 ✗ for i in 1:nEqn loop
1198 var_assigned := false;
1199 ✗ if UnorderedMap.contains(full.equation_names[i], unsolved_equations) then
1200 solve_opt := NONE();
1201 success := false;
1202 ✗ for cref in UnorderedSet.toList(full.occurrences[i]) loop
1203 ✗ stripped := ComponentRef.stripSubscriptsAll(cref);
1204 ✗ if UnorderedMap.contains(stripped, unsolved_inner_vars) then
1205 ✗ if isNone(solve_opt) then
1206 success := true;
1207 solve_opt := SOME(cref);
1208 else
1209 success := false;
1210 break;
1211 end if;
1212 end if;
1213 end for;
1214
1215 // ToDo: multi-components (algorithms)
1216 ():= match (solve_opt, success)
1217 // case I: possibly solvable as inner. check if the full cref can be solved
1218 case (SOME(solve_cref), true) algorithm
1219 // ToDo: for now assume it can be fully solved, needs to be checked!
1220 // ToDo: check solvability? --> if not linear then fail or check strictness
1221 ✗ stripped := ComponentRef.stripSubscriptsAll(solve_cref);
1222 ✗ solve_var := UnorderedMap.getSafe(stripped, unsolved_inner_vars, sourceInfo());
1223 ✗ solve_eqn := UnorderedMap.getSafe(full.equation_names[i], unsolved_equations, sourceInfo());
1224 ✗ inner_comps := StrongComponent.createSliceOrSingle(solve_cref, solve_var, solve_eqn) :: inner_comps;
1225
1226 // remove the variable and equation from candidates
1227 ✗ UnorderedMap.remove(stripped, unsolved_inner_vars);
1228 ✗ UnorderedMap.remove(full.equation_names[i], unsolved_equations);
1229 var_assigned := true;
1230 then ();
1231
1232 // case II: more than one inner found, just skip and do nothing until this might be solvable later
1233 case (SOME(solve_cref), false) then ();
1234
1235 // case III: none found, has to be residual
1236 case (NONE(), false) algorithm
1237 ✗ residuals := UnorderedMap.getSafe(full.equation_names[i], unsolved_equations, sourceInfo()) :: residuals;
1238 ✗ UnorderedMap.remove(full.equation_names[i], unsolved_equations);
1239 then ();
1240
1241 // FAIL: algorithm should not be able to produce this impossible combination
1242 else algorithm
1243 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Impossible result for equation representative: "
1244 + ComponentRef.toString(full.equation_names[i]) + "."});
1245 then ();
1246 end match;
1247 end if;
1248 ✗ if var_assigned then break; end if;
1249 end for;
1250
1251 // if not variable could be assigned in a full circle of checking all equations the problem is impossible to solve
1252 ✗ if not var_assigned then
1253 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Following variables could not be solved as inner variables:\n"
1254 + List.toString(UnorderedMap.valueList(unsolved_inner_vars), function Slice.toString(func = BVariable.pointerToString, maxLength = 10), List.Style.NEWLINE_TAB)});
1255 ✗ fail();
1256 end if;
1257 end while;
1258
1259 ✗ comp.mixed := List.any(inner_vars, function Slice.check(func = function BVariable.isDiscontinuous(staticAsContinuous = staticAsContinuous)));
1260
1261 // save residuals equations and iteration variables to the strong component
1262 ✗ strict.innerEquations := listArray(listReverse(inner_comps));
1263 ✗ strict.residual_eqns := listAppend(UnorderedMap.valueList(unsolved_equations), residuals);
1264 ✗ strict.iteration_vars := guru_vars;
1265 ✗ comp.strict := strict;
1266 end if;
1267
1268 then comp;
1269 else comp;
1270 end match;
1271 end guru;
1272
1273 function cellier extends Module.tearingInterface;
1274 // Cellier style tearing of what minimal tearing left over. Variables are only assigned as a whole
1275 // (the part of them that is in the loop), so arrays and records are never split any further.
1276 // The inner components of minimal tearing are kept as they are.
1277 protected
1278 Tearing strict, cellier_strict;
1279 algorithm
1280 comp := match (comp, full)
1281 case (StrongComponent.ALGEBRAIC_LOOP(strict = strict), Adjacency.FULL())
1282 guard(not listEmpty(strict.iteration_vars) and not listEmpty(strict.residual_eqns)) algorithm
1283 94 cellier_strict := cellierTearingSet(strict, full, equations, funcMap);
1284 // a linear loop is solved directly as linear system if its tearing set allows it, do not
1285 // replace such a tearing set by one that has to be solved as nonlinear system
1286
6/6
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 60 times.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 12 times.
✓ Branch 6 taken 17 times.
✓ Branch 7 taken 5 times.
94 if not (comp.linear and linearSystemCapable(strict) and not linearSystemCapable(cellier_strict)) then
1287 89 comp.strict := cellier_strict;
1288 end if;
1289 then comp;
1290 else comp;
1291 end match;
1292 end cellier;
1293
1294 function linearSystemCapable
1295 "true if the linear system codegen can handle the tearing set: no for equations in the inner
1296 equations, for equation residuals only if finalize splits them into rows (partial variables)
1297 and partial variables only if they are scalarized"
1298 input Tearing strict;
1299 output Boolean b;
1300 protected
1301 Boolean partial_vars = List.any(strict.iteration_vars, isPartialVarSlice);
1302 algorithm
1303
14/14
✓ Branch 0 taken 259 times.
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 259 times.
✓ Branch 3 taken 56 times.
✓ Branch 6 taken 51 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 36 times.
✓ Branch 9 taken 15 times.
✓ Branch 11 taken 34 times.
✓ Branch 12 taken 2 times.
✓ Branch 13 taken 15 times.
✓ Branch 14 taken 34 times.
✓ Branch 16 taken 5 times.
✓ Branch 17 taken 10 times.
315 b := not Array.any(strict.innerEquations, isForComponent)
1304 and (partial_vars or not List.any(list(Slice.getT(e) for e in strict.residual_eqns), Equation.isForEquation))
1305 and (not partial_vars or Flags.getConfigBool(Flags.SIM_CODE_SCALARIZE));
1306 end linearSystemCapable;
1307
1308 function isForComponent
1309 input StrongComponent comp;
1310 output Boolean b = List.any(StrongComponent.getEquations(comp), Equation.isForEquation);
1311 end isForComponent;
1312
1313 function cellierHasRow
1314 input Pointer<Equation> eqn;
1315 input UnorderedMap<ComponentRef, Integer> map;
1316 output Boolean b = UnorderedMap.contains(Equation.getEqnName(eqn), map);
1317 end cellierHasRow;
1318
1319 function cellierTearingSet
1320 input output Tearing strict;
1321 input Adjacency.Matrix full;
1322 input EquationPointers equations;
1323 input UnorderedMap<Path, Function> funcMap;
1324 protected
1325 UnorderedMap<Integer, Boolean> probes = UnorderedMap.new<Boolean>(Util.id, intEq);
1326 list<Pointer<Variable>> loop_vars, block_vars = {};
1327 list<Pointer<Equation>> loop_eqns;
1328 VariablePointers vars;
1329 EquationPointers eqns;
1330 Adjacency.Matrix adj;
1331 Adjacency.Mapping mapping;
1332 Integer nv, ne, nb, idx, start, size;
1333 array<Integer> unit_kind, eqn_glob, owner, eqn_step, row_var, block_step;
1334 array<Slice<VariablePointer>> var_slices;
1335 array<Slice<EquationPointer>> eqn_slices;
1336 array<list<Integer>> block_in, block_out, eqn_rows, eqn_units;
1337 array<StrongComponent> blocks;
1338 list<Integer> tears, fixed = {}, trial;
1339 Option<Integer> opt_idx;
1340 Boolean complete;
1341 list<tuple<Integer, StrongComponent>> ordered = {};
1342 UnorderedSet<Integer> tear_set;
1343 array<UnorderedSet<ComponentRef>> occurrences;
1344 array<UnorderedMap<ComponentRef, Solvability>> solvabilities;
1345 Slice<EquationPointer> eqn_slice;
1346 algorithm
1347 () := match full
1348 case Adjacency.FULL() algorithm
1349 94 occurrences := full.occurrences;
1350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 solvabilities := full.solvabilities;
1351 then ();
1352 else fail();
1353 end match;
1354
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 blocks := strict.innerEquations;
1355 nb := arrayLength(blocks);
1356
4/4
✓ Branch 0 taken 478 times.
✓ Branch 1 taken 94 times.
✓ Branch 2 taken 478 times.
✓ Branch 3 taken 94 times.
572 loop_vars := list(Slice.getT(var) for var in strict.iteration_vars);
1357
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 89 times.
101 for b in nb:-1:1 loop
1358 7 block_vars := listAppend(StrongComponent.getVariables(blocks[b]), block_vars);
1359 end for;
1360
4/4
✓ Branch 0 taken 614 times.
✓ Branch 1 taken 94 times.
✓ Branch 2 taken 614 times.
✓ Branch 3 taken 94 times.
708 loop_eqns := list(Slice.getT(eqn) for eqn in strict.residual_eqns);
1361 94 vars := VariablePointers.fromList(listAppend(loop_vars, block_vars));
1362 94 eqns := EquationPointers.fromList(loop_eqns);
1363 94 nv := VariablePointers.size(vars);
1364 94 ne := EquationPointers.size(eqns);
1365 // a variable or equation that occurs more than once can not be handled as one unit
1366
2/4
✓ Branch 2 taken 94 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 94 times.
94 if nv <> listLength(loop_vars) + listLength(block_vars) or ne <> listLength(loop_eqns) then
1367 ✗ return;
1368 end if;
1369 // equations without adjacency row (not in the equation map) can not be handled
1370
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 89 times.
101 for b in 1:nb loop
1371 7 loop_eqns := listAppend(StrongComponent.getEquations(blocks[b]), loop_eqns);
1372 end for;
1373
2/2
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 93 times.
94 if not List.all(loop_eqns, function cellierHasRow(map = equations.map)) then
1374 1 return;
1375 end if;
1376
4/4
✓ Branch 0 taken 602 times.
✓ Branch 1 taken 93 times.
✓ Branch 2 taken 602 times.
✓ Branch 3 taken 93 times.
695 loop_eqns := list(Slice.getT(eqn) for eqn in strict.residual_eqns);
1377
1378 // unit kinds: 1 = loop variable, 3 = solved by a block of minimal tearing
1379 93 unit_kind := arrayCreate(nv, 3);
1380 93 var_slices := arrayCreate(nv, listHead(strict.iteration_vars));
1381
2/2
✓ Branch 0 taken 466 times.
✓ Branch 1 taken 93 times.
559 for var in strict.iteration_vars loop
1382 466 idx := UnorderedMap.getSafe(BVariable.getVarName(Slice.getT(var)), vars.map, sourceInfo());
1383 466 unit_kind[idx] := 1;
1384 466 var_slices[idx] := var;
1385
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 466 times.
466 if Integer(BVariable.getTearingSelect(Slice.getT(var))) == Integer(NFBackendExtension.TearingSelect.ALWAYS) then
1386 fixed := idx :: fixed;
1387 end if;
1388 end for;
1389
1390 // scalar adjacency of the leftover system, only the rows of the loop count
1391
4/4
✓ Branch 0 taken 602 times.
✓ Branch 1 taken 93 times.
✓ Branch 2 taken 602 times.
✓ Branch 3 taken 93 times.
695 eqn_glob := listArray(list(UnorderedMap.getSafe(Equation.getEqnName(eqn), equations.map, sourceInfo()) for eqn in loop_eqns));
1392 93 adj := Adjacency.Matrix.subFull(full, arrayList(eqn_glob), eqns, vars);
1393 93 adj := Adjacency.Matrix.fullToFinal(adj, vars.map, eqns.map, eqns, NBAdjacency.MatrixStrictness.SORTING);
1394 93 mapping := Util.getOption(Adjacency.Matrix.getMappingOpt(adj));
1395 93 eqn_slices := listArray(strict.residual_eqns);
1396 93 eqn_rows := arrayCreate(ne, {});
1397
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
695 for i in 1:ne loop
1398 602 eqn_slice := eqn_slices[i];
1399 602 (start, size) := mapping.eqn_AtS[i];
1400
8/8
✓ Branch 0 taken 499 times.
✓ Branch 1 taken 103 times.
✓ Branch 2 taken 978 times.
✓ Branch 3 taken 499 times.
✓ Branch 6 taken 277 times.
✓ Branch 7 taken 103 times.
✓ Branch 8 taken 277 times.
✓ Branch 9 taken 103 times.
1857 eqn_rows[i] := if listEmpty(eqn_slice.indices) then list(start + k for k in 0:size - 1)
1401 else list(start + k for k in List.sort(eqn_slice.indices, intGt));
1402 end for;
1403
1404 // blocks of minimal tearing keep their order, they wait for the loop variables they depend on
1405 93 owner := arrayCreate(nv, 0);
1406 93 block_in := arrayCreate(nb, {});
1407 93 block_out := arrayCreate(nb, {});
1408
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 88 times.
100 for b in 1:nb loop
1409
4/4
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 10 times.
✓ Branch 5 taken 7 times.
17 block_out[b] := list(UnorderedMap.getSafe(BVariable.getVarName(v), vars.map, sourceInfo()) for v in StrongComponent.getVariables(blocks[b]));
1410
2/2
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 7 times.
17 for o in block_out[b] loop owner[o] := b; end for;
1411 end for;
1412
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 88 times.
100 for b in 1:nb loop
1413
2/2
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 7 times.
14 for eqn in StrongComponent.getEquations(blocks[b]) loop
1414
2/2
✓ Branch 4 taken 17 times.
✓ Branch 5 taken 7 times.
24 for cref in UnorderedSet.toList(occurrences[UnorderedMap.getSafe(Equation.getEqnName(eqn), equations.map, sourceInfo())]) loop
1415 17 opt_idx := UnorderedMap.get(ComponentRef.stripSubscriptsAll(cref), vars.map);
1416
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 if isSome(opt_idx) then
1417 17 SOME(idx) := opt_idx;
1418
3/4
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 10 times.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
17 if owner[idx] < b and not listMember(idx, block_in[b]) then
1419 14 block_in[b] := idx :: block_in[b];
1420 end if;
1421 end if;
1422 end for;
1423 end for;
1424 end for;
1425
1426 // greedy tearing, then try to remove every tear variable again, biggest first
1427 93 (tears, _, _, _, _, _) := cellierCausalize(fixed, true, adj, vars, eqns, eqn_glob, eqn_rows, solvabilities, unit_kind, var_slices, block_in, block_out, funcMap, probes);
1428
2/2
✓ Branch 3 taken 138 times.
✓ Branch 4 taken 93 times.
231 for t in List.sort(tears, function cellierSizeLess(var_slices = var_slices)) loop
1429
1/2
✓ Branch 1 taken 138 times.
✗ Branch 2 not taken.
138 if not listMember(t, fixed) then
1430
6/6
✓ Branch 0 taken 138 times.
✓ Branch 1 taken 262 times.
✓ Branch 2 taken 400 times.
✓ Branch 3 taken 138 times.
✓ Branch 4 taken 262 times.
✓ Branch 5 taken 138 times.
538 trial := list(i for i guard(i <> t) in tears);
1431 138 (_, _, _, _, _, complete) := cellierCausalize(trial, false, adj, vars, eqns, eqn_glob, eqn_rows, solvabilities, unit_kind, var_slices, block_in, block_out, funcMap, probes);
1432
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 123 times.
138 if complete then tears := trial; end if;
1433 end if;
1434 end for;
1435 93 (tears, eqn_step, eqn_units, row_var, block_step, complete) := cellierCausalize(tears, false, adj, vars, eqns, eqn_glob, eqn_rows, solvabilities, unit_kind, var_slices, block_in, block_out, funcMap, probes);
1436
1437
3/4
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 85 times.
93 if not complete or Array.all(eqn_step, function intEq(i2 = 0)) then
1438 8 return;
1439 end if;
1440
1441 // new inner components in causal order, interleaved with the blocks of minimal tearing
1442
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
671 for i in 1:ne loop
1443
2/2
✓ Branch 1 taken 436 times.
✓ Branch 2 taken 150 times.
586 if eqn_step[i] > 0 then
1444 436 ordered := (eqn_step[i], cellierComponent(i, eqn_units[i], eqn_rows[i], row_var, mapping, vars, eqns, var_slices, eqn_slices, solvabilities[eqn_glob[i]])) :: ordered;
1445 end if;
1446 end for;
1447
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 82 times.
88 for b in 1:nb loop
1448 3 ordered := (block_step[b], blocks[b]) :: ordered;
1449 end for;
1450 85 ordered := List.sort(ordered, function Util.compareTupleIntGt());
1451
1452 85 tear_set := UnorderedSet.fromList(tears, Util.id, intEq);
1453
16/16
✓ Branch 0 taken 439 times.
✓ Branch 1 taken 85 times.
✓ Branch 2 taken 439 times.
✓ Branch 3 taken 85 times.
✓ Branch 10 taken 343 times.
✓ Branch 11 taken 111 times.
✓ Branch 12 taken 454 times.
✓ Branch 13 taken 85 times.
✓ Branch 14 taken 111 times.
✓ Branch 15 taken 85 times.
✓ Branch 20 taken 436 times.
✓ Branch 21 taken 150 times.
✓ Branch 22 taken 586 times.
✓ Branch 23 taken 85 times.
✓ Branch 24 taken 150 times.
✓ Branch 25 taken 85 times.
1564 strict.innerEquations := listArray(list(Util.tuple22(tpl) for tpl in ordered));
1454 strict.iteration_vars := list(var for var guard(UnorderedSet.contains(UnorderedMap.getSafe(BVariable.getVarName(Slice.getT(var)), vars.map, sourceInfo()), tear_set)) in strict.iteration_vars);
1455 strict.residual_eqns := list(eqn for eqn guard(eqn_step[UnorderedMap.getSafe(Equation.getEqnName(Slice.getT(eqn)), eqns.map, sourceInfo())] == 0) in strict.residual_eqns);
1456 end cellierTearingSet;
1457
1458 function cellierComponent
1459 "creates the inner component of an assignment like sorting does"
1460 input Integer e;
1461 input list<Integer> units;
1462 input list<Integer> rows;
1463 input array<Integer> row_var;
1464 input Adjacency.Mapping mapping;
1465 input VariablePointers vars;
1466 input EquationPointers eqns;
1467 input array<Slice<VariablePointer>> var_slices;
1468 input array<Slice<EquationPointer>> eqn_slices;
1469 input UnorderedMap<ComponentRef, Solvability> solvabilities;
1470 output StrongComponent comp;
1471 protected
1472 Integer u;
1473 ComponentRef name;
1474 algorithm
1475
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 436 times.
436 if not List.hasOneElement(units) then
1476 ✗ comp := StrongComponent.MULTI_COMPONENT(list(var_slices[i] for i in units), eqn_slices[e], NBSolve.Status.UNPROCESSED);
1477 elseif List.hasOneElement(rows) then
1478 362 comp := StrongComponent.createPseudoScalar(rows, row_var, mapping, vars, eqns);
1479 else
1480 74 u := listHead(units);
1481 74 name := BVariable.getVarName(VariablePointers.getVarAt(vars, u));
1482 74 comp := StrongComponent.createPseudoSlice(u, e, listHead(cellierOccurrences(name, solvabilities)), rows, row_var, eqns, mapping,
1483 Equation.isArrayEquation(EquationPointers.getEqnAt(eqns, e)));
1484 end if;
1485 end cellierComponent;
1486
1487 function cellierSizeLess
1488 "sorts bigger units first"
1489 input Integer u1;
1490 input Integer u2;
1491 input array<Slice<VariablePointer>> var_slices;
1492 output Boolean b = Slice.size(var_slices[u1], function BVariable.size(resize = false)) < Slice.size(var_slices[u2], function BVariable.size(resize = false));
1493 end cellierSizeLess;
1494
1495 function cellierCausalize
1496 "causalizes the leftover system for the given tear variables. greedy chooses new tear
1497 variables whenever it gets stuck, otherwise it stops and reports the system incomplete."
1498 input list<Integer> fixed_tears;
1499 input Boolean greedy;
1500 input Adjacency.Matrix adj;
1501 input VariablePointers vars;
1502 input EquationPointers eqns;
1503 input array<Integer> eqn_glob;
1504 input array<list<Integer>> eqn_rows;
1505 input array<UnorderedMap<ComponentRef, Solvability>> solvabilities;
1506 input array<Integer> unit_kind;
1507 input array<Slice<VariablePointer>> var_slices;
1508 input array<list<Integer>> block_in;
1509 input array<list<Integer>> block_out;
1510 input UnorderedMap<Path, Function> funcMap;
1511 input UnorderedMap<Integer, Boolean> probes "cached solver checks";
1512 output list<Integer> tears = fixed_tears;
1513 output array<Integer> eqn_step;
1514 output array<list<Integer>> eqn_units;
1515 output array<Integer> row_var;
1516 output array<Integer> block_step;
1517 output Boolean complete = true;
1518 protected
1519 Adjacency.IntMatrix m, mT;
1520 Adjacency.Mapping mapping;
1521 array<Integer> m_data, mT_data, unknown_rem, eqn_unknown, eqn_size, stamp;
1522 array<Boolean> known_scal, row_in_loop;
1523 array<list<tuple<Integer, list<Integer>>>> ready = arrayCreate(4, {});
1524 array<list<Integer>> partial_eqns;
1525 array<Integer> partial_cov, partial_rank, cover_owner;
1526 Boolean is_partial, overlap;
1527 Integer nv, ne, nb = arrayLength(block_in), start, size, open_units = 0, step = 0, next_block = 1, stamp_id = 0;
1528 Integer rank, best = 0, u;
1529 list<Integer> fresh = {}, units = {};
1530 Boolean found;
1531 Slice<VariablePointer> slice;
1532 algorithm
1533 () := match adj
1534 case Adjacency.Matrix.FINAL() algorithm
1535 324 m := adj.m; mT := adj.mT; mapping := adj.mapping;
1536 then ();
1537 else fail();
1538 end match;
1539 324 m_data := Adjacency.IntMatrix.entries(m);
1540 324 mT_data := Adjacency.IntMatrix.entries(mT);
1541
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 324 times.
324 nv := arrayLength(mapping.var_AtS);
1542
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 324 times.
324 ne := arrayLength(mapping.eqn_AtS);
1543 324 eqn_step := arrayCreate(ne, 0);
1544 324 eqn_units := arrayCreate(ne, {});
1545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 324 times.
648 row_var := arrayCreate(arrayLength(mapping.eqn_StA), -1);
1546 324 block_step := arrayCreate(nb, 0);
1547
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 324 times.
648 stamp := arrayCreate(arrayLength(mapping.var_StA), 0);
1548 324 partial_eqns := arrayCreate(nv, {});
1549 324 partial_cov := arrayCreate(nv, 0);
1550 324 partial_rank := arrayCreate(nv, 0);
1551
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 324 times.
648 cover_owner := arrayCreate(arrayLength(mapping.var_StA), 0);
1552
1553 // elements of partially contained variables that are not in the loop are known
1554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 324 times.
648 known_scal := arrayCreate(arrayLength(mapping.var_StA), false);
1555 324 unknown_rem := arrayCreate(nv, 0);
1556
1/2
✓ Branch 0 taken 324 times.
✗ Branch 1 not taken.
2798 for i in 1:nv loop
1557 2474 (start, size) := mapping.var_AtS[i];
1558 2474 slice := var_slices[i];
1559
4/4
✓ Branch 1 taken 2444 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 751 times.
✓ Branch 4 taken 1693 times.
2474 if unit_kind[i] == 1 and not listEmpty(slice.indices) then
1560
1/2
✓ Branch 0 taken 751 times.
✗ Branch 1 not taken.
5401 for k in 0:size - 1 loop known_scal[start + k] := true; end for;
1561
2/2
✓ Branch 1 taken 2095 times.
✓ Branch 2 taken 751 times.
2846 for k in slice.indices loop known_scal[start + k] := false; end for;
1562 751 unknown_rem[i] := listLength(slice.indices);
1563 else
1564 1723 unknown_rem[i] := size;
1565 end if;
1566
3/4
✓ Branch 1 taken 2444 times.
✓ Branch 2 taken 30 times.
✓ Branch 4 taken 2444 times.
✗ Branch 5 not taken.
2474 if unit_kind[i] <> 3 and unknown_rem[i] > 0 then
1567 2444 open_units := open_units + 1;
1568 end if;
1569 end for;
1570
1571 // unknown entries per equation, an equation can only be assigned if every row has exactly one
1572
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 324 times.
648 row_in_loop := arrayCreate(arrayLength(mapping.eqn_StA), false);
1573 324 eqn_unknown := arrayCreate(ne, 0);
1574 324 eqn_size := arrayCreate(ne, 0);
1575
1/2
✓ Branch 0 taken 324 times.
✗ Branch 1 not taken.
3435 for e in 1:ne loop
1576 3111 eqn_size[e] := listLength(eqn_rows[e]);
1577
2/2
✓ Branch 1 taken 5846 times.
✓ Branch 2 taken 3111 times.
8957 for r in eqn_rows[e] loop
1578 5846 row_in_loop[r] := true;
1579
2/2
✓ Branch 2 taken 5762 times.
✓ Branch 3 taken 84 times.
20676 for p in m.start[r]:m.start[r] + m.len[r] - 1 loop
1580
2/2
✓ Branch 2 taken 14588 times.
✓ Branch 3 taken 242 times.
14830 if not known_scal[m_data[p]] then
1581 14588 eqn_unknown[e] := eqn_unknown[e] + 1;
1582 end if;
1583 end for;
1584 end for;
1585 end for;
1586
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 324 times.
3435 for e in ne:-1:1 loop
1587
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 3111 times.
3111 if eqn_unknown[e] == eqn_size[e] then fresh := e :: fresh; end if;
1588 end for;
1589
1590
2/2
✓ Branch 0 taken 385 times.
✓ Branch 1 taken 324 times.
709 for t in fixed_tears loop
1591 385 fresh := cellierMarkKnown(t, mapping, mT, mT_data, row_in_loop, known_scal, unknown_rem, eqn_unknown, eqn_size, eqn_step, fresh);
1592 385 open_units := open_units - 1;
1593 end for;
1594
1595 while true loop
1596 // fire all blocks of minimal tearing that have their inputs
1597
4/4
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 1855 times.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 14 times.
1881 while next_block <= nb and List.all(block_in[next_block], function cellierIsKnown(unknown_rem = unknown_rem)) loop
1598 14 step := step + 1;
1599 14 block_step[next_block] := step;
1600
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 14 times.
34 for o in block_out[next_block] loop
1601 20 fresh := cellierMarkKnown(o, mapping, mT, mT_data, row_in_loop, known_scal, unknown_rem, eqn_unknown, eqn_size, eqn_step, fresh);
1602 end for;
1603 14 next_block := next_block + 1;
1604 end while;
1605
1606 // check the new candidates
1607
2/2
✓ Branch 0 taken 2426 times.
✓ Branch 1 taken 1867 times.
4293 for e in fresh loop
1608 2426 stamp_id := stamp_id + 1;
1609 2426 (rank, units, is_partial) := cellierCanAssign(e, eqn_rows[e], eqn_size[e], mapping, m, m_data, known_scal, unknown_rem, eqn_unknown, unit_kind, stamp, stamp_id, row_var, vars, eqns, solvabilities, eqn_glob, funcMap, probes);
1610
4/4
✓ Branch 0 taken 2038 times.
✓ Branch 1 taken 388 times.
✓ Branch 2 taken 1508 times.
✓ Branch 3 taken 530 times.
2426 if rank > 0 and not is_partial then
1611 3016 ready[rank] := (e, units) :: ready[rank];
1612 elseif rank > 0 then
1613 // collect equations that solve distinct elements of the unit, the unit is assigned once they cover all of it
1614 530 u := listHead(units);
1615 530 overlap := List.any(eqn_rows[e], function cellierIsCovered(row_var = row_var, cover_owner = cover_owner));
1616
1/2
✓ Branch 0 taken 530 times.
✗ Branch 1 not taken.
530 if not overlap then
1617
2/2
✓ Branch 3 taken 530 times.
✓ Branch 4 taken 530 times.
1060 for r in eqn_rows[e] loop cover_owner[row_var[r]] := e; end for;
1618 1060 partial_eqns[u] := e :: partial_eqns[u];
1619 530 partial_cov[u] := partial_cov[u] + eqn_size[e];
1620 530 partial_rank[u] := max(partial_rank[u], rank);
1621
2/2
✓ Branch 2 taken 90 times.
✓ Branch 3 taken 440 times.
530 if partial_cov[u] == unknown_rem[u] then
1622 180 ready[partial_rank[u]] := (-u, units) :: ready[partial_rank[u]];
1623 end if;
1624 end if;
1625 end if;
1626 end for;
1627 fresh := {};
1628
1629 // assign the best valid candidate, a candidate stays valid as long as its unknowns do not change
1630 found := false;
1631 2060 for r in 1:4 loop
1632
4/4
✓ Branch 0 taken 3658 times.
✓ Branch 1 taken 90 times.
✓ Branch 3 taken 1598 times.
✓ Branch 4 taken 2060 times.
3748 while not found and not listEmpty(ready[r]) loop
1633 1598 (best, units) := listHead(ready[r]);
1634 1598 ready[r] := listRest(ready[r]);
1635
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 1508 times.
1598 if best < 0 then
1636 90 found := unknown_rem[-best] > 0;
1637 elseif eqn_step[best] == 0 and eqn_unknown[best] == eqn_size[best] then
1638 found := true;
1639 end if;
1640 end while;
1641
2/2
✓ Branch 0 taken 2060 times.
✓ Branch 1 taken 1405 times.
3465 if found then break; end if;
1642 end for;
1643
1644
2/2
✓ Branch 0 taken 1405 times.
✓ Branch 1 taken 462 times.
1867 if found then
1645 1405 step := step + 1;
1646
4/4
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 1315 times.
✓ Branch 4 taken 1831 times.
✓ Branch 5 taken 1405 times.
4641 for e in (if best < 0 then partial_eqns[-best] else {best}) loop
1647 1831 eqn_step[e] := step;
1648 1831 eqn_units[e] := units;
1649 end for;
1650
2/2
✓ Branch 0 taken 1405 times.
✓ Branch 1 taken 1405 times.
2810 for un in units loop
1651 1405 fresh := cellierMarkKnown(un, mapping, mT, mT_data, row_in_loop, known_scal, unknown_rem, eqn_unknown, eqn_size, eqn_step, fresh);
1652 1405 open_units := open_units - 1;
1653 end for;
1654 elseif open_units == 0 then
1655 break;
1656 elseif greedy then
1657 138 u := cellierSelectTear(mapping, mT, mT_data, row_in_loop, known_scal, unknown_rem, unit_kind, eqn_step, vars);
1658 tears := u :: tears;
1659 138 fresh := cellierMarkKnown(u, mapping, mT, mT_data, row_in_loop, known_scal, unknown_rem, eqn_unknown, eqn_size, eqn_step, fresh);
1660 138 open_units := open_units - 1;
1661 else
1662 complete := false;
1663 break;
1664 end if;
1665 end while;
1666 end cellierCausalize;
1667
1668 function cellierIsCovered
1669 input Integer r;
1670 input array<Integer> row_var;
1671 input array<Integer> cover_owner;
1672 output Boolean b = cover_owner[row_var[r]] <> 0;
1673 end cellierIsCovered;
1674
1675 function cellierIsKnown
1676 input Integer u;
1677 input array<Integer> unknown_rem;
1678 output Boolean b = unknown_rem[u] == 0;
1679 end cellierIsKnown;
1680
1681 function cellierMarkKnown
1682 "marks all scalars of a unit known and collects the equations that might be assignable now"
1683 input Integer u;
1684 input Adjacency.Mapping mapping;
1685 input Adjacency.IntMatrix mT;
1686 input array<Integer> mT_data;
1687 input array<Boolean> row_in_loop;
1688 input array<Boolean> known_scal;
1689 input array<Integer> unknown_rem;
1690 input array<Integer> eqn_unknown;
1691 input array<Integer> eqn_size;
1692 input array<Integer> eqn_step;
1693 input output list<Integer> fresh;
1694 protected
1695 Integer start, size, e, r;
1696 algorithm
1697 1948 (start, size) := mapping.var_AtS[u];
1698
1/2
✓ Branch 0 taken 1948 times.
✗ Branch 1 not taken.
8514 for s in start:start + size - 1 loop
1699
2/2
✓ Branch 1 taken 4661 times.
✓ Branch 2 taken 1905 times.
6566 if not known_scal[s] then
1700 4661 arrayUpdate(known_scal, s, true);
1701
2/2
✓ Branch 2 taken 4651 times.
✓ Branch 3 taken 10 times.
16686 for p in mT.start[s]:mT.start[s] + mT.len[s] - 1 loop
1702 12025 r := mT_data[p];
1703
2/2
✓ Branch 1 taken 11859 times.
✓ Branch 2 taken 166 times.
12025 if row_in_loop[r] then
1704 11859 e := mapping.eqn_StA[r];
1705 11859 arrayUpdate(eqn_unknown, e, eqn_unknown[e] - 1);
1706
3/4
✓ Branch 2 taken 2426 times.
✓ Branch 3 taken 9433 times.
✓ Branch 5 taken 2426 times.
✗ Branch 6 not taken.
11859 if eqn_unknown[e] == eqn_size[e] and eqn_step[e] == 0 then
1707 fresh := e :: fresh;
1708 end if;
1709 end if;
1710 end for;
1711 end if;
1712 end for;
1713 1948 arrayUpdate(unknown_rem, u, 0);
1714 end cellierMarkKnown;
1715
1716 function cellierCanAssign
1717 "returns the solvability rank (0 if not assignable) and the units an equation can be solved for.
1718 every row needs exactly one unknown scalar, all distinct and together covering the units."
1719 input Integer e;
1720 input list<Integer> rows;
1721 input Integer size;
1722 input Adjacency.Mapping mapping;
1723 input Adjacency.IntMatrix m;
1724 input array<Integer> m_data;
1725 input array<Boolean> known_scal;
1726 input array<Integer> unknown_rem;
1727 input array<Integer> eqn_unknown;
1728 input array<Integer> unit_kind;
1729 input array<Integer> stamp;
1730 input Integer stamp_id;
1731 input array<Integer> row_var;
1732 input VariablePointers vars;
1733 input EquationPointers eqns;
1734 input array<UnorderedMap<ComponentRef, Solvability>> solvabilities;
1735 input array<Integer> eqn_glob;
1736 input UnorderedMap<Path, Function> funcMap;
1737 input UnorderedMap<Integer, Boolean> probes;
1738 output Integer rank = 0;
1739 output list<Integer> units = {};
1740 output Boolean is_partial = false "the rows only cover a part of the unit";
1741 protected
1742 Integer cnt, sv = 0, u, covered = 0, key;
1743 ComponentRef name;
1744 algorithm
1745
2/2
✓ Branch 1 taken 142 times.
✓ Branch 2 taken 2284 times.
2426 if eqn_unknown[e] <> size then return; end if;
1746
2/2
✓ Branch 0 taken 3826 times.
✓ Branch 1 taken 2258 times.
6084 for r in rows loop
1747 cnt := 0;
1748
1/2
✓ Branch 2 taken 3826 times.
✗ Branch 3 not taken.
13885 for p in m.start[r]:m.start[r] + m.len[r] - 1 loop
1749
2/2
✓ Branch 2 taken 3838 times.
✓ Branch 3 taken 6221 times.
10059 if not known_scal[m_data[p]] then
1750 3838 cnt := cnt + 1;
1751 sv := m_data[p];
1752 end if;
1753 end for;
1754
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3814 times.
3826 if cnt <> 1 then
1755 units := {};
1756 12 return;
1757 elseif stamp[sv] == stamp_id then
1758 units := {};
1759 12 return;
1760 end if;
1761 3802 arrayUpdate(stamp, sv, stamp_id);
1762 3802 arrayUpdate(row_var, r, sv);
1763 3802 u := mapping.var_StA[sv];
1764
2/2
✓ Branch 1 taken 2272 times.
✓ Branch 2 taken 1530 times.
3802 if not listMember(u, units) then
1765
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2270 times.
2272 if unit_kind[u] <> 1 then
1766 units := {};
1767 2 return;
1768 end if;
1769 units := u :: units;
1770 2270 covered := covered + unknown_rem[u];
1771 end if;
1772 end for;
1773 // a single unit can also be covered by several equations together
1774
3/4
✓ Branch 0 taken 534 times.
✓ Branch 1 taken 1724 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 534 times.
2258 is_partial := covered > size and List.hasOneElement(units);
1775
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2258 times.
2258 if covered <> size and not is_partial then
1776 units := {};
1777 ✗ return;
1778 end if;
1779
1780
1/2
✓ Branch 1 taken 2258 times.
✗ Branch 2 not taken.
2258 if List.hasOneElement(units) then
1781 2258 name := BVariable.getVarName(VariablePointers.getVarAt(vars, listHead(units)));
1782 2258 rank := cellierRank(name, solvabilities[eqn_glob[e]]);
1783
2/2
✓ Branch 0 taken 2038 times.
✓ Branch 1 taken 220 times.
2258 if rank < 1 or rank > 4 then
1784 rank := 0;
1785 else
1786 // make sure the solver can actually do it
1787 2038 key := e * (arrayLength(unit_kind) + 1) + listHead(units);
1788
2/2
✓ Branch 1 taken 544 times.
✓ Branch 2 taken 1494 times.
2038 if not UnorderedMap.contains(key, probes) then
1789
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 544 times.
544 UnorderedMap.add(key, cellierSolvable(EquationPointers.getEqnAt(eqns, e), name, solvabilities[eqn_glob[e]], funcMap), probes);
1790 end if;
1791
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2038 times.
2038 if not UnorderedMap.getSafe(key, probes, sourceInfo()) then rank := 0; end if;
1792 end if;
1793 elseif isRecordAssignment(EquationPointers.getEqnAt(eqns, e), list(VariablePointers.getVarAt(vars, i) for i in units)) then
1794 rank := 1;
1795 end if;
1796 if rank == 0 then units := {}; end if;
1797 end cellierCanAssign;
1798
1799 function cellierOccurrences
1800 "all crefs of a variable as they occur in an equation"
1801 input ComponentRef name;
1802 input UnorderedMap<ComponentRef, Solvability> solvabilities;
1803 output list<ComponentRef> crefs = list(c for c guard(ComponentRef.isEqual(ComponentRef.stripSubscriptsAll(c), name)) in UnorderedMap.keyList(solvabilities));
1804 end cellierOccurrences;
1805
1806 function cellierSolvable
1807 "checks with the solver if the equation can be solved explicitly for the only occurrence of the variable"
1808 input Pointer<Equation> eqn_ptr;
1809 input ComponentRef name;
1810 input UnorderedMap<ComponentRef, Solvability> solvabilities;
1811 input UnorderedMap<Path, Function> funcMap;
1812 output Boolean b = false;
1813 protected
1814 list<ComponentRef> crefs = cellierOccurrences(name, solvabilities);
1815 Equation eqn = Pointer.access(eqn_ptr);
1816 Solve.Status status;
1817 Boolean single;
1818 algorithm
1819 (eqn, single) := match eqn
1820 local
1821 Equation body;
1822 case Equation.FOR_EQUATION(body = {body}) then (body, true);
1823 case Equation.FOR_EQUATION() then (eqn, false);
1824 else (eqn, true);
1825 end match;
1826
1/2
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
544 if single and List.hasOneElement(crefs) then
1827 544 ErrorExt.setCheckpoint(getInstanceName());
1828 try
1829 544 (_, status, _) := Solve.solveBody(eqn, listHead(crefs), funcMap);
1830 544 b := status == NBSolve.Status.EXPLICIT;
1831 else
1832 b := false;
1833 end try;
1834 544 ErrorExt.rollBack(getInstanceName());
1835 end if;
1836 end cellierSolvable;
1837
1838 function cellierRank
1839 "worst solvability rank of all occurrences of a variable, for equations also keyed by subscripted crefs"
1840 input ComponentRef name;
1841 input UnorderedMap<ComponentRef, Solvability> solvabilities;
1842 output Integer rank = 0;
1843 protected
1844 Integer r;
1845 algorithm
1846
2/2
✓ Branch 1 taken 8359 times.
✓ Branch 2 taken 2258 times.
10617 for tpl in UnorderedMap.toList(solvabilities) loop
1847
2/2
✓ Branch 3 taken 2258 times.
✓ Branch 4 taken 6101 times.
8359 if ComponentRef.isEqual(ComponentRef.stripSubscriptsAll(Util.tuple21(tpl)), name) then
1848 2258 r := Solvability.rank(Util.tuple22(tpl));
1849 // unknown solvability makes the whole variable unknown
1850
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2258 times.
2258 if r == 0 then
1851 rank := 0;
1852 ✗ return;
1853 end if;
1854 rank := max(rank, r);
1855 end if;
1856 end for;
1857 end cellierRank;
1858
1859 function isRecordAssignment
1860 "true if the record equation has all variables (or their record parents) on the lhs and none on the rhs"
1861 input Pointer<Equation> eqn;
1862 input list<Pointer<Variable>> vars;
1863 output Boolean b = true;
1864 protected
1865 UnorderedSet<ComponentRef> lhs, rhs;
1866 list<ComponentRef> names;
1867 algorithm
1868 () := match Pointer.access(eqn)
1869 local
1870 Equation e;
1871 case e as Equation.RECORD_EQUATION() algorithm
1872 ✗ lhs := UnorderedSet.fromList(list(ComponentRef.stripSubscriptsAll(c) for c in UnorderedSet.toList(Expression.extractCrefs(e.lhs))), ComponentRef.hash, ComponentRef.isEqual);
1873 ✗ rhs := UnorderedSet.fromList(list(ComponentRef.stripSubscriptsAll(c) for c in UnorderedSet.toList(Expression.extractCrefs(e.rhs))), ComponentRef.hash, ComponentRef.isEqual);
1874 ✗ for var in vars loop
1875 ✗ names := recordNames(var);
1876 ✗ if not List.any(names, function UnorderedSet.contains(set = lhs)) or List.any(names, function UnorderedSet.contains(set = rhs)) then
1877 b := false;
1878 end if;
1879 end for;
1880 then ();
1881 else algorithm b := false; then ();
1882 end match;
1883 end isRecordAssignment;
1884
1885 function recordNames
1886 "the variable name and the names of all its record parents"
1887 input Pointer<Variable> var;
1888 output list<ComponentRef> names = {BVariable.getVarName(var)};
1889 algorithm
1890 names := match BVariable.getParent(var)
1891 local
1892 Pointer<Variable> parent;
1893 ✗ case SOME(parent) then listAppend(names, recordNames(parent));
1894 else names;
1895 end match;
1896 end recordNames;
1897
1898 function cellierSelectTear
1899 "chooses the unknown unit with the best tearing select, then a start value, then most open equations, then smallest size"
1900 input Adjacency.Mapping mapping;
1901 input Adjacency.IntMatrix mT;
1902 input array<Integer> mT_data;
1903 input array<Boolean> row_in_loop;
1904 input array<Boolean> known_scal;
1905 input array<Integer> unknown_rem;
1906 input array<Integer> unit_kind;
1907 input array<Integer> eqn_step;
1908 input VariablePointers vars;
1909 output Integer best = 0;
1910 protected
1911 Integer start, size, cls, occ, e, best_cls = -1, best_occ = -1, best_size = 0;
1912 Boolean has_start, best_start = false;
1913 array<Integer> seen = arrayCreate(arrayLength(eqn_step), 0);
1914 algorithm
1915
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 138 times.
1660 for u in 1:arrayLength(unknown_rem) loop
1916
4/4
✓ Branch 1 taken 1512 times.
✓ Branch 2 taken 10 times.
✓ Branch 4 taken 1070 times.
✓ Branch 5 taken 442 times.
1522 if unit_kind[u] <> 3 and unknown_rem[u] > 0 then
1917 1070 cls := Integer(BVariable.getTearingSelect(VariablePointers.getVarAt(vars, u)));
1918 // a tear variable without start value would start the iteration at zero
1919
3/4
✗ Branch 2 not taken.
✓ Branch 3 taken 1070 times.
✓ Branch 8 taken 546 times.
✓ Branch 9 taken 524 times.
1070 has_start := isSome(BVariable.getStartAttribute(VariablePointers.getVarAt(vars, u)));
1920 occ := 0;
1921 1070 (start, size) := mapping.var_AtS[u];
1922
1/2
✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
4626 for s in start:start + size - 1 loop
1923
2/2
✓ Branch 1 taken 2391 times.
✓ Branch 2 taken 1165 times.
3556 if not known_scal[s] then
1924
1/2
✓ Branch 2 taken 2391 times.
✗ Branch 3 not taken.
8702 for p in mT.start[s]:mT.start[s] + mT.len[s] - 1 loop
1925
2/2
✓ Branch 2 taken 6225 times.
✓ Branch 3 taken 86 times.
6311 if row_in_loop[mT_data[p]] then
1926 6225 e := mapping.eqn_StA[mT_data[p]];
1927
3/4
✓ Branch 1 taken 6225 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3238 times.
✓ Branch 5 taken 2987 times.
6225 if eqn_step[e] == 0 and seen[e] <> u then
1928 3238 seen[e] := u;
1929 3238 occ := occ + 1;
1930 end if;
1931 end if;
1932 end for;
1933 end if;
1934 end for;
1935
13/14
✓ Branch 0 taken 932 times.
✓ Branch 1 taken 138 times.
✓ Branch 2 taken 932 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 905 times.
✓ Branch 5 taken 27 times.
✓ Branch 6 taken 709 times.
✓ Branch 7 taken 196 times.
✓ Branch 8 taken 653 times.
✓ Branch 9 taken 56 times.
✓ Branch 10 taken 311 times.
✓ Branch 11 taken 342 times.
✓ Branch 13 taken 3 times.
✓ Branch 14 taken 308 times.
1070 if cls > best_cls or (cls == best_cls and ((has_start and not best_start) or (has_start == best_start
1936 and (occ > best_occ or (occ == best_occ and unknown_rem[u] < best_size))))) then
1937 224 best := u; best_cls := cls; best_start := has_start; best_occ := occ; best_size := unknown_rem[u];
1938 end if;
1939 end if;
1940 end for;
1941 end cellierSelectTear;
1942
1943 function checkLinearity
1944 input Adjacency.Matrix full;
1945 input UnorderedMap<ComponentRef, Integer> v "variables in the algebraic loop";
1946 input UnorderedMap<ComponentRef, Integer> e "equations in the algebraic loop";
1947 output Boolean linear;
1948 protected
1949 function varIsLinear
1950 input ComponentRef var;
1951 input UnorderedMap<ComponentRef, Integer> v;
1952 input UnorderedMap<ComponentRef, Solvability> sol;
1953 output Boolean b = not (UnorderedMap.contains(var, v) and Solvability.isNonlinearOrImplicit(UnorderedMap.getSafe(var, sol, sourceInfo())));
1954 end varIsLinear;
1955
1956 function eqnIsLinear
1957 input Integer i "equation index";
1958 input array<UnorderedSet<ComponentRef>> occ;
1959 input array<UnorderedMap<ComponentRef, Solvability>> sol;
1960 input UnorderedMap<ComponentRef, Integer> v;
1961 output Boolean b = UnorderedSet.all(occ[i], function varIsLinear(v = v, sol = sol[i]));
1962 end eqnIsLinear;
1963 algorithm
1964 linear := match full
1965 96 case Adjacency.Matrix.FULL() then UnorderedMap.all(e, function eqnIsLinear(occ = full.occurrences, sol = full.solvabilities, v = v));
1966 else algorithm
1967 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " expected type full, got type " + Adjacency.strictnessString(Adjacency.Matrix.getStrictness(full)) + "."});
1968 ✗ then fail();
1969 end match;
1970 end checkLinearity;
1971
1972 function filterDiscreteVariables
1973 "splits off all discrete variables. also splits off variables that belong to a record with a discrete variable in this algebraic loop"
1974 input list<Pointer<Variable>> vars_lst;
1975 input Boolean staticAsContinuous;
1976 output list<Pointer<Variable>> cont_vars;
1977 output list<Pointer<Variable>> disc_vars;
1978 protected
1979 UnorderedSet<ComponentRef> discrete_records = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
1980 list<Pointer<Variable>> rec_disc_vars;
1981
1982 function addDiscreteRecord
1983 "checks if it has a record parent that needs to be added"
1984 input Pointer<Variable> var;
1985 input UnorderedSet<ComponentRef> discrete_records;
1986 algorithm
1987 () := match BVariable.getParent(var)
1988 local
1989 Pointer<Variable> parent;
1990 case SOME(parent) algorithm
1991 ✗ UnorderedSet.add(BVariable.getVarName(parent), discrete_records);
1992 ✗ addDiscreteRecord(parent, discrete_records);
1993 then ();
1994 else ();
1995 end match;
1996 end addDiscreteRecord;
1997
1998 function checkDiscreteRecord
1999 "checks if continuous variable is part of records of which discretes are in this loop"
2000 input Pointer<Variable> var;
2001 input UnorderedSet<ComponentRef> discrete_records;
2002 input Boolean is_parent;
2003 output Boolean b;
2004 algorithm
2005 b := match BVariable.getParent(var)
2006 local
2007 Pointer<Variable> parent;
2008 62 case SOME(parent) then checkDiscreteRecord(parent, discrete_records, true);
2009
3/4
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 1457 times.
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
1519 else is_parent and UnorderedSet.contains(BVariable.getVarName(var), discrete_records);
2010 end match;
2011 end checkDiscreteRecord;
2012 algorithm
2013 // basic filter all discrete variables
2014
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 51 times.
141 (cont_vars, disc_vars) := List.splitOnTrue(vars_lst, function BVariable.isContinuous(staticAsContinuous = staticAsContinuous));
2015 // add all records that contain discrete variables
2016
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 96 times.
103 for var in disc_vars loop addDiscreteRecord(var, discrete_records); end for;
2017 // split off all variables that are part of records of which discretes are in this loop
2018 96 (rec_disc_vars, cont_vars) := List.splitOnTrue(cont_vars, function checkDiscreteRecord(discrete_records = discrete_records, is_parent = false));
2019
2020 // add the continous record variables that might be solved alongside discretes to the list
2021 96 disc_vars := listReverse(listAppend(rec_disc_vars, disc_vars));
2022 end filterDiscreteVariables;
2023
2024 function getImpliedInnerVars
2025 "returns all implied inner variables if the equation is solved. necessary if e.g. trying to solve a single discrete output
2026 from an algorithm. The full algorithm and all outputs need to be made inner variables."
2027 input Pointer<Equation> eqn;
2028 output list<Pointer<Variable>> vars;
2029 algorithm
2030 vars := match Pointer.access(eqn)
2031 local
2032 Algorithm alg;
2033 Expression tpl;
2034
2035 case Equation.ALGORITHM(alg = alg) algorithm
2036
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 6 times.
18 vars := list(BVariable.getVarPointer(out_cr, sourceInfo()) for out_cr in alg.outputs);
2037 then vars;
2038
2039 // skip the placeholders of omitted outputs, e.g. (_, y) = f(x)
2040 case Equation.RECORD_EQUATION(lhs = tpl as Expression.TUPLE()) algorithm
2041 ✗ then list(BVariable.getVarPointer(tpl_cr, sourceInfo()) for tpl_cr guard(not (ComponentRef.isWild(tpl_cr) or ComponentRef.isEmpty(tpl_cr)))
2042 in UnorderedSet.toList(Expression.extractCrefs(tpl)));
2043
2044 case Equation.RECORD_EQUATION(lhs = Expression.CREF()) algorithm
2045 // ToDo: if vars contains any child of cref add all children of cref
2046 then {};
2047
2048 else {};
2049 end match;
2050 end getImpliedInnerVars;
2051
2052 annotation(__OpenModelica_Interface="nbackend");
2053 end NBTearing;
2054
2055