Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 47.2% 169 / 0 / 358
Functions: -% 0 / 1 / 1
Branches: 45.4% 138 / 0 / 304

OMCompiler/Compiler/NBackEnd/Classes/NBStrongComponent.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 NBStrongComponent
37 "file: NBStrongComponent.mo
38 package: NBStrongComponent
39 description: This file contains the data-types used save the strong Component
40 data after causalization.
41 "
42 public
43 import NBResizable.EvalOrder;
44
45 protected
46 // selfimport
47 import StrongComponent = NBStrongComponent;
48
49 // NF imports
50 import ComponentRef = NFComponentRef;
51 import Dimension = NFDimension;
52 import Expression = NFExpression;
53 import Subscript = NFSubscript;
54 import Type = NFType;
55 import Variable = NFVariable;
56
57 // Backend imports
58 import Adjacency = NBAdjacency;
59 import NBAdjacency.Mapping;
60 import BackendDAE = NBackendDAE;
61 import Causalize = NBCausalize;
62 import BVariable = NBVariable;
63 import NBEquation.{Equation, EquationPointer, EquationPointers, EquationAttributes, Iterator, IfEquationBody};
64 import Initialization = NBInitialization;
65 import Inline = NBInline;
66 import NBJacobian.JacobianType;
67 import Matching = NBMatching;
68 import Resizable = NBResizable;
69 import Solve = NBSolve;
70 import Sorting = NBSorting;
71 import NBSorting.SuperNode;
72 import BPartition = NBPartition;
73 import NBPartition.{Partition};
74 import Tearing = NBTearing;
75 import NBVariable.{VariablePointer, VariablePointers};
76
77 // Util imports
78 import Pointer;
79 import Slice = NBSlice;
80 import StringUtil;
81 import UnorderedMap;
82 import UnorderedSet;
83
84 public
85 uniontype AliasInfo
86 record ALIAS_INFO
87 BPartition.Kind kind "The partition kind";
88 Integer partitionIndex "the partition index";
89 Integer componentIndex "The index in that strong component array";
90 end ALIAS_INFO;
91
92 function toString
93 input AliasInfo info;
94 output String str = Partition.kindToString(info.kind) + "[" + intString(info.partitionIndex) + " | " + intString(info.componentIndex) + "]";
95 end toString;
96
97 function hash
98 input AliasInfo info;
99 output Integer i = stringHashDjb2(toString(info));
100 end hash;
101
102 function isEqual
103 input AliasInfo info1;
104 input AliasInfo info2;
105 output Boolean b = (info1.componentIndex == info2.componentIndex) and (info1.partitionIndex == info2.partitionIndex) and (info1.kind == info2.kind);
106 end isEqual;
107 end AliasInfo;
108
109 record SINGLE_COMPONENT
110 "component for all equations that solve for a single (possibly multidimensional) variable
111 SCALAR_EQUATION, ARRAY_EQUATION, RECORD_EQUATION."
112 Pointer<Variable> var;
113 Pointer<Equation> eqn;
114 Solve.Status status;
115 end SINGLE_COMPONENT;
116
117 record MULTI_COMPONENT
118 "component for all equations that can solve for more than one variable instance
119 ALGORITHM, WHEN_EQUATION, IF_EQUATION"
120 list<Slice<VariablePointer>> vars;
121 Slice<EquationPointer> eqn;
122 Solve.Status status;
123 end MULTI_COMPONENT;
124
125 record SLICED_COMPONENT
126 "component for all equations AND/OR variables that need to be sliced (zero based indices)"
127 ComponentRef var_cref "cref to solve for";
128 Slice<VariablePointer> var "sliced variable";
129 Slice<EquationPointer> eqn "sliced equation";
130 Solve.Status status;
131 end SLICED_COMPONENT;
132
133 record RESIZABLE_COMPONENT
134 "component for for-equations with trivial evaluation order"
135 ComponentRef var_cref "cref to solve for";
136 Slice<VariablePointer> var "sliced variable";
137 Slice<EquationPointer> eqn "sliced equation";
138 UnorderedMap<ComponentRef, EvalOrder> order "independent, forward, backward";
139 Solve.Status status;
140 end RESIZABLE_COMPONENT;
141
142 record GENERIC_COMPONENT
143 "component for all equations that need to be sliced but where no for-loop could be recovered
144 has no status since this is generated by the Solve module and is always status=EXPLICIT."
145 ComponentRef var_cref "cref to solve for";
146 Slice<VariablePointer> var "sliced variable";
147 Slice<EquationPointer> eqn "sliced equation";
148 end GENERIC_COMPONENT;
149
150 record ENTWINED_COMPONENT
151 "component for entwined equations that have to be called in a specific interleaved order
152 but do not form an algebraic loop. Slices can be SLICED_COMPONENT, GENERIC_COMPONENT,
153 RESIZABLE_COMPONENT, SINGLE_COMPONENT or MULTI_COMPONENT."
154 list<StrongComponent> entwined_slices "one entry per distinct equation (for-loop or scalar)";
155 list<tuple<Pointer<Equation>, Integer>> entwined_tpl_lst "equation with scalar idx (0 based) - fallback scalarization";
156 end ENTWINED_COMPONENT;
157
158 record ALGEBRAIC_LOOP
159 "component for equations that have to be solved as a system."
160 Integer idx;
161 Tearing strict;
162 Option<Tearing> casual;
163 Boolean linear "true if the loop is linear";
164 Boolean mixed "true for systems that have discrete variables";
165 Boolean homotopy "true if contains homotopy()";
166 Solve.Status status;
167 Boolean implicitlyCreated "true if this component was promoted straight from a
168 single/multi/resizable component by NBSolve.mo's
169 Tearing.implicit() rather than found and torn by
170 NBTearing.mo's own tearing pass. The two are numbered
171 (idx) via separate counters that can coincide, so this
172 flag lets the generated Jacobian's name stay unique.";
173 end ALGEBRAIC_LOOP;
174
175 record ALIAS
176 "Component representing equal strong components in ODE<->INIT<->DAE
177 has no status since this is generated by the Solve module and is always status=EXPLICIT."
178 AliasInfo aliasInfo "The strong component array and index it refers to";
179 StrongComponent original "The original strong component for analysis";
180 end ALIAS;
181
182 function toString
183 input StrongComponent comp;
184 input Integer index = -1 "negative indices will not be printed";
185 output String str;
186 protected
187 Integer s = StrongComponent.size(comp, true);
188 String indexStr = if index > 0 then " " + intString(index) else "";
189 algorithm
190 str := match comp
191
192 case SINGLE_COMPONENT() algorithm
193 18 str := StringUtil.headline_3("BLOCK" + indexStr + ": Single Strong Component (status = " + Solve.statusString(comp.status) + ", size = " + intString(s) + ")");
194 18 str := str + "### Variable:\n" + Variable.toString(Pointer.access(comp.var), "\t") + "\n";
195 18 str := str + "### Equation:\n" + Equation.toString(Pointer.access(comp.eqn), "\t") + "\n";
196 then str;
197
198 case MULTI_COMPONENT() algorithm
199 6 str := StringUtil.headline_3("BLOCK" + indexStr + ": Multi Strong Component (status = " + Solve.statusString(comp.status) + ", size = " + intString(s) + ")");
200 6 str := str + "### Variables:\n";
201 6 str := str + List.toString(comp.vars, function Slice.toString(func = BVariable.pointerToString, maxLength = 10), List.Style.NEWLINE_TAB);
202 6 str := str + "\n### Equation:\n" + Slice.toString(comp.eqn, function Equation.pointerToString(str = "\t")) + "\n";
203 then str;
204
205 case SLICED_COMPONENT() algorithm
206
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 str := if index == -2 then "" else StringUtil.headline_3("BLOCK" + indexStr + ": Sliced Component (status = " + Solve.statusString(comp.status) + ", size = " + intString(s) + ")");
207 11 str := str + "### Variable:\n\t" + ComponentRef.toString(comp.var_cref) + "\n";
208 11 str := str + "### Equation:\n" + Slice.toString(comp.eqn, function Equation.pointerToString(str = "\t")) + "\n";
209 then str;
210
211 case RESIZABLE_COMPONENT() algorithm
212 26 str := StringUtil.headline_3("BLOCK" + indexStr + ": Resizable Component (status = " + Solve.statusString(comp.status) + ", size = " + intString(s) + ")");
213 26 str := str + "### Variable:\n\t" + ComponentRef.toString(comp.var_cref) + "\n";
214 26 str := str + "### Equation:\n" + Equation.pointerToString(Slice.getT(comp.eqn), "\t") + "\n";
215 then str;
216
217 case ENTWINED_COMPONENT() algorithm
218 ✗ str := StringUtil.headline_3("BLOCK" + indexStr + ": Entwined Component (status = Solve.EXPLICIT, size = " + intString(s) + ")");
219 ✗ str := str + "call order: " + List.toString(list(Equation.getEqnName(Util.tuple21(e)) for e in comp.entwined_tpl_lst), ComponentRef.toString, List.Style.FLAT_CURLY_SHORT) + "\n";
220 ✗ str := str + List.toString(comp.entwined_slices, function toString(index = -2), List.Style.NONE);
221 then str;
222
223 case GENERIC_COMPONENT() algorithm
224 ✗ str := StringUtil.headline_3("BLOCK" + indexStr + ": Generic Component (status = Solve.EXPLICIT, size = " + intString(s) + ")");
225 ✗ str := str + "### Variable:\n\t" + ComponentRef.toString(comp.var_cref) + "\n";
226 ✗ str := str + "### Equation:\n" + Slice.toString(comp.eqn, function Equation.pointerToString(str = "\t")) + "\n";
227 then str;
228
229 case ALGEBRAIC_LOOP() algorithm
230
3/6
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
12 str := StringUtil.headline_3("BLOCK" + indexStr + ": Algebraic Loop (Linear = " + boolString(comp.linear) + ", Mixed = " + boolString(comp.mixed) + ", Homotopy = " + boolString(comp.homotopy) + ", size = " + intString(s) + ")");
231 4 str := str + Tearing.toString(comp.strict, "Strict Tearing Set");
232
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if isSome(comp.casual) then
233 ✗ str := str + Tearing.toString(Util.getOption(comp.casual), "Casual Tearing Set");
234 end if;
235 then str;
236
237 case ALIAS() algorithm
238 16 str := "--- Alias of " + AliasInfo.toString(comp.aliasInfo) + " ---\n" + toString(comp.original, index);
239 then str;
240
241 else algorithm
242 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."});
243 ✗ then fail();
244 end match;
245 end toString;
246
247 uniontype CountCollector
248 record COUNT_COLLECTOR
249 Integer single_scalar;
250 Integer single_array;
251 Integer single_record;
252 Integer multi_algorithm;
253 Integer multi_when;
254 Integer multi_if;
255 Integer multi_tpl;
256 Integer resizable_for;
257 Integer generic_for;
258 Integer entwined_for;
259 Integer loop_lin;
260 Integer loop_nlin;
261 end COUNT_COLLECTOR;
262 end CountCollector;
263
264 function strongComponentInfo
265 input output StrongComponent comp;
266 input Pointer<CountCollector> collector_ptr;
267 protected
268 CountCollector collector = Pointer.access(collector_ptr);
269 algorithm
270 () := match comp
271 case SINGLE_COMPONENT() algorithm
272 () := match Pointer.access(comp.eqn)
273 ✗ case Equation.SCALAR_EQUATION() algorithm collector.single_scalar := collector.single_scalar + 1; Pointer.update(collector_ptr, collector); then ();
274 ✗ case Equation.ARRAY_EQUATION() algorithm collector.single_array := collector.single_array + 1; Pointer.update(collector_ptr, collector); then ();
275 ✗ case Equation.RECORD_EQUATION() algorithm collector.single_record := collector.single_record + 1; Pointer.update(collector_ptr, collector); then ();
276 ✗ else algorithm Error.addCompilerWarning("Cannot classify strong component:\n" + toString(comp) + "\n"); then ();
277 end match;
278 then ();
279
280 case MULTI_COMPONENT() algorithm
281 () := match Pointer.access(Slice.getT(comp.eqn))
282 ✗ case Equation.ALGORITHM() algorithm collector.multi_algorithm := collector.multi_algorithm + 1; Pointer.update(collector_ptr, collector); then ();
283 ✗ case Equation.WHEN_EQUATION() algorithm collector.multi_when := collector.multi_when + 1; Pointer.update(collector_ptr, collector); then ();
284 ✗ case Equation.IF_EQUATION() algorithm collector.multi_if := collector.multi_if + 1; Pointer.update(collector_ptr, collector); then ();
285 ✗ case Equation.RECORD_EQUATION() algorithm collector.multi_tpl := collector.multi_tpl + 1; Pointer.update(collector_ptr, collector); then ();
286 ✗ else algorithm Error.addCompilerWarning("Cannot classify strong component:\n" + toString(comp) + "\n"); then ();
287 end match;
288 then ();
289
290 case SLICED_COMPONENT() algorithm
291 () := match Pointer.access(Slice.getT(comp.eqn))
292 ✗ case Equation.SCALAR_EQUATION() algorithm collector.single_scalar := collector.single_scalar + 1; Pointer.update(collector_ptr, collector); then ();
293 ✗ case Equation.ARRAY_EQUATION() algorithm collector.single_array := collector.single_array + 1; Pointer.update(collector_ptr, collector); then ();
294 ✗ case Equation.RECORD_EQUATION() algorithm collector.single_record := collector.single_record + 1; Pointer.update(collector_ptr, collector); then ();
295 ✗ else algorithm Error.addCompilerWarning("Cannot classify strong component:\n" + toString(comp) + "\n"); then ();
296 end match;
297 then ();
298
299 ✗ case RESIZABLE_COMPONENT() algorithm collector.resizable_for := collector.resizable_for +1; Pointer.update(collector_ptr, collector); then ();
300 ✗ case GENERIC_COMPONENT() algorithm collector.generic_for := collector.generic_for + 1; Pointer.update(collector_ptr, collector); then ();
301 ✗ case ENTWINED_COMPONENT() algorithm collector.entwined_for := collector.entwined_for + 1; Pointer.update(collector_ptr, collector); then ();
302 ✗ case ALGEBRAIC_LOOP() guard(comp.linear) algorithm collector.loop_lin := collector.loop_lin + 1; Pointer.update(collector_ptr, collector); then ();
303 ✗ case ALGEBRAIC_LOOP() algorithm collector.loop_nlin := collector.loop_nlin + 1; Pointer.update(collector_ptr, collector); then ();
304 ✗ case ALIAS() algorithm strongComponentInfo(comp.original, collector_ptr); then ();
305 ✗ else algorithm Error.addCompilerWarning("Cannot classify strong component:\n" + toString(comp) + "\n"); then ();
306 end match;
307 end strongComponentInfo;
308
309 function hash
310 "only hashes basic types, isEqual is used to differ between sliced/entwined loops"
311 input StrongComponent comp;
312 output Integer i;
313 algorithm
314 i := match comp
315 5005 case SINGLE_COMPONENT() then BVariable.hash(comp.var) + Equation.hash(comp.eqn);
316 299 case MULTI_COMPONENT() then Equation.hash(Slice.getT(comp.eqn));
317 3096 case SLICED_COMPONENT() then ComponentRef.hash(comp.var_cref) + Equation.hash(Slice.getT(comp.eqn));
318 777 case RESIZABLE_COMPONENT()then ComponentRef.hash(comp.var_cref) + Equation.hash(Slice.getT(comp.eqn));
319 ✗ case GENERIC_COMPONENT() then Equation.hash(Slice.getT(comp.eqn));
320
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 4 times.
16 case ENTWINED_COMPONENT() then sum(hash(sub_comp) for sub_comp in comp.entwined_slices);
321 171 case ALGEBRAIC_LOOP() then Tearing.hash(comp.strict);
322 ✗ case ALIAS() then AliasInfo.hash(comp.aliasInfo);
323 else algorithm
324 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."});
325 ✗ then fail();
326 end match;
327 end hash;
328
329 function isEqual
330 input StrongComponent comp1;
331 input StrongComponent comp2;
332 output Boolean b;
333 algorithm
334 b := match(comp1, comp2)
335
4/4
✓ Branch 1 taken 1631 times.
✓ Branch 2 taken 430 times.
✓ Branch 4 taken 52 times.
✓ Branch 5 taken 1579 times.
2061 case (SINGLE_COMPONENT(), SINGLE_COMPONENT()) then BVariable.equalName(comp1.var, comp2.var) and Equation.isEqualPtr(comp1.eqn, comp2.eqn);
336 57 case (MULTI_COMPONENT(), MULTI_COMPONENT()) then Equation.isEqualPtr(Slice.getT(comp1.eqn), Slice.getT(comp2.eqn));
337
3/4
✓ Branch 1 taken 909 times.
✓ Branch 2 taken 36 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 909 times.
945 case (SLICED_COMPONENT(), SLICED_COMPONENT()) then ComponentRef.isEqual(comp1.var_cref, comp2.var_cref) and Slice.isEqual(comp1.eqn, comp2.eqn, Equation.isEqualPtr);
338
3/4
✓ Branch 1 taken 235 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 233 times.
235 case (RESIZABLE_COMPONENT(), RESIZABLE_COMPONENT()) then ComponentRef.isEqual(comp1.var_cref, comp2.var_cref) and Slice.isEqual(comp1.eqn, comp2.eqn, Equation.isEqualPtr);
339 ✗ case (GENERIC_COMPONENT(), GENERIC_COMPONENT()) then Slice.isEqual(comp1.eqn, comp2.eqn, Equation.isEqualPtr);
340 2 case (ENTWINED_COMPONENT(), ENTWINED_COMPONENT()) then List.isEqualOnTrue(comp1.entwined_slices, comp2.entwined_slices, isEqual);
341 49 case (ALGEBRAIC_LOOP(), ALGEBRAIC_LOOP()) then Tearing.isEqual(comp1.strict, comp2.strict);
342 ✗ case (ALIAS(), ALIAS()) then AliasInfo.isEqual(comp1.aliasInfo, comp2.aliasInfo);
343 else false;
344 end match;
345 end isEqual;
346
347 function size
348 input StrongComponent comp;
349 input Boolean resize;
350 output Integer s;
351 algorithm
352 s := match comp
353 27 case SINGLE_COMPONENT() then Equation.size(comp.eqn, resize);
354
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 case MULTI_COMPONENT() then Slice.size(comp.eqn, function Equation.size(resize = resize));
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 case SLICED_COMPONENT() then Slice.size(comp.eqn, function Equation.size(resize = resize));
356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 case RESIZABLE_COMPONENT() then Slice.size(comp.eqn, function Equation.size(resize = resize));
357 ✗ case GENERIC_COMPONENT() then Slice.size(comp.eqn, function Equation.size(resize = resize));
358 ✗ case ENTWINED_COMPONENT() then sum(StrongComponent.size(c, resize) for c in comp.entwined_slices);
359 4 case ALGEBRAIC_LOOP() then Tearing.size(comp.strict, resize);
360 16 case ALIAS() then StrongComponent.size(comp.original, resize);
361 else algorithm
362 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed. Cannot determine size of strong component:\n" + toString(comp) + "\n"});
363 ✗ then fail();
364 end match;
365 end size;
366
367 function removeAlias
368 input output StrongComponent comp;
369 algorithm
370 comp := match comp
371 74 case ALIAS() then comp.original;
372 else comp;
373 end match;
374 end removeAlias;
375
376 function solvesInsideReduction
377 "true if a scalar for-equation is solved for a cref with a whole dimension, e.g. x[i, :] for
378 y[i] = sum(x[i, j] for j in 1:n) matched to x[i, 1]. The cref does not determine the solved
379 elements, they have to be solved one by one. A whole dimension of an array equation inside
380 the for-equation, e.g. a[i, :] = b * i, determines them."
381 input Pointer<Equation> eqn_ptr;
382 input ComponentRef cref;
383 output Boolean b;
384 algorithm
385 b := match Pointer.access(eqn_ptr)
386 local
387 Equation body;
388 // a size one array equation, e.g. a[i, :] = b[1:1, i], determines the elements as well
389
5/6
✓ Branch 2 taken 565 times.
✓ Branch 3 taken 47 times.
✓ Branch 6 taken 556 times.
✓ Branch 7 taken 9 times.
✓ Branch 10 taken 556 times.
✗ Branch 11 not taken.
612 case Equation.FOR_EQUATION(body = {body}) then Equation.size(Pointer.create(body)) == 1
390 and not Type.isArray(Equation.getType(body))
391 and List.any(ComponentRef.subscriptsAllFlat(cref), Subscript.isWhole);
392 else false;
393 end match;
394 end solvesInsideReduction;
395
396 function createPseudoSlice
397 input Integer var_arr_idx;
398 input Integer eqn_arr_idx;
399 input ComponentRef cref_to_solve;
400 input list<Integer> eqn_scal_indices;
401 input array<Integer> eqn_to_var;
402 input EquationPointers eqns;
403 input Adjacency.Mapping mapping;
404 input Boolean independent = false "true if scalar equations can be solved in any order";
405 output StrongComponent comp;
406 protected
407 Pointer<Variable> var_ptr;
408 Pointer<Equation> eqn_ptr;
409 Integer first_var, var_size, first_eqn, eqn_size;
410 Slice<VariablePointer>var_slice;
411 Slice<EquationPointer>eqn_slice;
412 list<Integer> var_scal_indices;
413 UnorderedMap<ComponentRef, EvalOrder> order;
414 algorithm
415 // get and save sliced variable and equation
416 1129 var_ptr := BVariable.getVarPointer(cref_to_solve, sourceInfo());
417 1129 eqn_ptr := EquationPointers.getEqnAt(eqns, eqn_arr_idx);
418 1129 (first_var, var_size) := mapping.var_AtS[var_arr_idx];
419 1129 (first_eqn, eqn_size) := mapping.eqn_AtS[eqn_arr_idx];
420
4/4
✓ Branch 0 taken 9034 times.
✓ Branch 1 taken 1129 times.
✓ Branch 2 taken 9034 times.
✓ Branch 3 taken 1129 times.
10163 var_scal_indices := list(eqn_to_var[e] for e in eqn_scal_indices);
421
422 // check if the full variable occurs and its independent
423
7/8
✓ Branch 0 taken 1027 times.
✓ Branch 1 taken 102 times.
✓ Branch 3 taken 533 times.
✓ Branch 4 taken 494 times.
✓ Branch 6 taken 533 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 488 times.
✓ Branch 10 taken 45 times.
1129 if independent and Equation.isArrayEquation(eqn_ptr) and listLength(eqn_scal_indices) == eqn_size and listLength(var_scal_indices) == var_size then
424 488 var_slice := Slice.SLICE(var_ptr, {});
425 488 eqn_slice := Slice.SLICE(eqn_ptr, {});
426 else
427
4/4
✓ Branch 0 taken 6583 times.
✓ Branch 1 taken 641 times.
✓ Branch 2 taken 6583 times.
✓ Branch 3 taken 641 times.
7224 var_slice := Slice.SLICE(var_ptr, list(idx - first_var for idx in var_scal_indices));
428
4/4
✓ Branch 0 taken 6583 times.
✓ Branch 1 taken 641 times.
✓ Branch 2 taken 6583 times.
✓ Branch 3 taken 641 times.
7224 eqn_slice := Slice.SLICE(eqn_ptr, list(idx - first_eqn for idx in eqn_scal_indices));
429 end if;
430
431 // check if it is a resizable component. a variable inside a reduction can only be solved as a slice
432 1129 order := Resizable.detect(Pointer.access(eqn_ptr), cref_to_solve);
433
5/6
✓ Branch 2 taken 589 times.
✓ Branch 3 taken 540 times.
✓ Branch 5 taken 533 times.
✓ Branch 6 taken 56 times.
✓ Branch 8 taken 533 times.
✗ Branch 9 not taken.
1129 if not List.any(UnorderedMap.valueList(order), Resizable.orderFailed) and listLength(eqn_scal_indices) == eqn_size
434 and not solvesInsideReduction(eqn_ptr, cref_to_solve) then
435 533 comp := RESIZABLE_COMPONENT(
436 var_cref = cref_to_solve,
437 var = var_slice,
438 eqn = eqn_slice,
439 order = order,
440 status = NBSolve.Status.UNPROCESSED);
441 else
442 596 comp := createSliceOrSingle(cref_to_solve, var_slice, eqn_slice);
443 end if;
444 end createPseudoSlice;
445
446 function createPseudoEntwined
447 input list<Integer> eqn_indices;
448 input array<Integer> eqn_to_var;
449 input Mapping mapping;
450 input VariablePointers vars;
451 input EquationPointers eqns;
452 input list<SuperNode> nodes;
453 output StrongComponent entwined;
454 protected
455 UnorderedMap<Integer, Slice.IntLst> elem_map = UnorderedMap.new<Slice.IntLst>(Util.id, intEq);
456 UnorderedMap<Integer, ComponentRef> cref_map = UnorderedMap.new<ComponentRef>(Util.id, intEq);
457 Integer eqn_arr_idx, var_arr_idx;
458 Slice.IntLst scal_indices;
459 list<StrongComponent> entwined_slices = {};
460 list<tuple<Pointer<Equation>, Integer>> entwined_tpl_lst;
461 algorithm
462 // collect individual buckets again
463
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 2 times.
38 for idx in eqn_indices loop
464 72 UnorderedMap.add(mapping.eqn_StA[idx], idx :: UnorderedMap.getOrDefault(mapping.eqn_StA[idx], elem_map, {}), elem_map);
465 end for;
466 // collect crefs to solve for
467
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 for node in nodes loop
468 () := match node
469 case SuperNode.ARRAY_BUCKET() algorithm
470 6 UnorderedMap.add(node.arr_idx, node.cref_to_solve, cref_map);
471 then ();
472 else ();
473 end match;
474 end for;
475
476 // create individual slices (array bucket → slice component, scalar → scalar component)
477
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2 times.
8 for tpl in UnorderedMap.toList(elem_map) loop
478 6 (eqn_arr_idx, scal_indices) := tpl;
479 // the calls of the slice consume its indices in order, see the call order below
480 6 scal_indices := listReverse(scal_indices);
481
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if UnorderedMap.contains(eqn_arr_idx, cref_map) then
482 6 var_arr_idx := mapping.var_StA[eqn_to_var[Util.tuple21(mapping.eqn_AtS[eqn_arr_idx])]];
483 6 entwined_slices := createPseudoSlice(var_arr_idx, eqn_arr_idx, UnorderedMap.getSafe(eqn_arr_idx, cref_map, sourceInfo()), scal_indices, eqn_to_var, eqns, mapping) :: entwined_slices;
484 else
485 ✗ entwined_slices := createPseudoScalar(scal_indices, eqn_to_var, mapping, vars, eqns) :: entwined_slices;
486 end if;
487 end for;
488
489 // create scalar list for fallback
490
4/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 2 times.
38 entwined_tpl_lst := list((EquationPointers.getEqnAt(eqns, mapping.eqn_StA[idx]), idx) for idx in eqn_indices);
491
492 2 entwined := ENTWINED_COMPONENT(entwined_slices, entwined_tpl_lst);
493 end createPseudoEntwined;
494
495 function createAlias
496 input BPartition.Kind kind;
497 input Integer partitionIndex;
498 input Pointer<Integer> index_ptr;
499 input StrongComponent orig_comp;
500 output StrongComponent alias_comp;
501 algorithm
502 3325 alias_comp := ALIAS(ALIAS_INFO(kind, partitionIndex, Pointer.access(index_ptr)), orig_comp);
503 3325 Pointer.update(index_ptr, Pointer.access(index_ptr) + 1);
504 end createAlias;
505
506 function createPseudoEntwinedIndices
507 input array<list<Integer>> entwined_indices;
508 input EquationPointers eqns;
509 input Adjacency.Mapping mapping;
510 output list<tuple<Pointer<Equation>, Integer>> flat_tpl_indices = {};
511 protected
512 Integer arr_idx, first_idx;
513 array<Integer> eqn_StA "safe access with iterated integer (void pointer)";
514 algorithm
515 ✗ for tmp in entwined_indices loop
516 ✗ for scal_idx in tmp loop
517 ✗ eqn_StA := mapping.eqn_StA;
518 ✗ arr_idx := eqn_StA[scal_idx];
519 ✗ (first_idx, _) := mapping.eqn_AtS[arr_idx];
520 ✗ flat_tpl_indices := (EquationPointers.getEqnAt(eqns, arr_idx), scal_idx-first_idx) :: flat_tpl_indices;
521 end for;
522 end for;
523 ✗ flat_tpl_indices := listReverse(flat_tpl_indices);
524 end createPseudoEntwinedIndices;
525
526 type DAEType = enumeration(UNPROCESSED, REMOVED, INNER, RESIDUAL);
527
528 function sortDAEModeComponents
529 input output Option<array<StrongComponent>> comps;
530 input VariablePointers variables;
531 input Pointer<Integer> uniqueIndex;
532 protected
533 list<StrongComponent> residuals = {}, inners = {};
534 // used to determine if a sliced equation will be handled as residual or as inner
535 UnorderedSet<ComponentRef> slice_set = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
536 algorithm
537 comps := match comps
538 local
539 array<StrongComponent> original;
540
541 case SOME(original) algorithm
542
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
2 for comp in original loop
543 1 (residuals, inners) := sortDAEModeComponent(comp, residuals, inners, variables, uniqueIndex, slice_set);
544 end for;
545
546 /* order of inners matters */
547 1 comps := SOME(listArray(listAppend(listReverse(inners), residuals)));
548 then comps;
549
550 else comps;
551 end match;
552 end sortDAEModeComponents;
553
554 function sortDAEModeComponent
555 input StrongComponent comp;
556 input output list<StrongComponent> residuals;
557 input output list<StrongComponent> inners;
558 input VariablePointers variables;
559 input Pointer<Integer> uniqueIndex;
560 input UnorderedSet<ComponentRef> slice_set;
561 protected
562 list<StrongComponent> new_residuals;
563 DAEType dae_type;
564 algorithm
565 (new_residuals, dae_type) := match comp
566 // single equation fully solved for single variable (not neccessarily scalar)
567 case SINGLE_COMPONENT() algorithm
568 1 (new_residuals, dae_type) := singleDAEModeComponent(comp.eqn, variables, uniqueIndex);
569 then (new_residuals, dae_type);
570
571 case MULTI_COMPONENT() algorithm
572 ✗ (new_residuals, dae_type) := slicedDAEModeComponent(comp.vars, {comp.eqn}, variables, uniqueIndex, slice_set);
573 then (new_residuals, dae_type);
574
575 case SLICED_COMPONENT() algorithm
576 // this will always result in inner equation for now as either eqn or var are sliced
577 // -> in the future improve this
578 ✗ (new_residuals, dae_type) := slicedDAEModeComponent({comp.var}, {comp.eqn}, variables, uniqueIndex, slice_set);
579 then (new_residuals, dae_type);
580
581 case RESIZABLE_COMPONENT() algorithm
582 ✗ (new_residuals, dae_type) := slicedDAEModeComponent({comp.var}, {comp.eqn}, variables, uniqueIndex, slice_set);
583 then (new_residuals, dae_type);
584
585 case GENERIC_COMPONENT() algorithm
586 ✗ (new_residuals, dae_type) := slicedDAEModeComponent({comp.var}, {comp.eqn}, variables, uniqueIndex, slice_set);
587 then (new_residuals, dae_type);
588
589 case ALGEBRAIC_LOOP() algorithm
590 ✗ (new_residuals, dae_type) := slicedDAEModeComponent(comp.strict.iteration_vars, comp.strict.residual_eqns, variables, uniqueIndex, slice_set);
591 then (new_residuals, dae_type);
592
593 ✗ else ({}, if isDiscrete(comp) then DAEType.REMOVED else DAEType.INNER);
594 end match;
595
596
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if dae_type == DAEType.RESIDUAL then
597 // add residuals
598 1 residuals := listAppend(new_residuals, residuals);
599 elseif dae_type == DAEType.INNER then
600 // add original to inners
601 inners := comp :: inners;
602 end if;
603 end sortDAEModeComponent;
604
605 function slicedDAEModeComponent
606 input list<Slice<Pointer<Variable>>> var_slices;
607 input list<Slice<Pointer<Equation>>> eqn_slices;
608 input VariablePointers variables;
609 input Pointer<Integer> uniqueIndex;
610 input UnorderedSet<ComponentRef> slice_set;
611 output list<StrongComponent> new_residuals;
612 output DAEType dae_type = DAEType.RESIDUAL;
613 protected
614 Pointer<Equation> eqn;
615 ComponentRef eqn_name;
616 list<list<StrongComponent>> acc_new_residuals = {};
617 algorithm
618 ✗ if List.all(list(v.indices for v in var_slices), listEmpty) then
619 ✗ for eqn_slice in eqn_slices loop
620 ✗ eqn := Slice.getT(eqn_slice);
621 ✗ eqn_name := Equation.getEqnName(eqn);
622 ✗ if listEmpty(eqn_slice.indices) and not UnorderedSet.contains(eqn_name, slice_set) then
623 // unsliced equation in multi component / equation not found in map
624 ✗ (new_residuals, dae_type) := singleDAEModeComponent(eqn, variables, uniqueIndex);
625 ✗ if dae_type == DAEType.RESIDUAL then
626 acc_new_residuals := new_residuals :: acc_new_residuals;
627 elseif dae_type == DAEType.INNER then
628 break;
629 end if;
630 else
631 // this sliced equation cannot be made residual, add it to the map
632 ✗ dae_type := DAEType.INNER;
633 ✗ break;
634 end if;
635 end for;
636 else
637 ✗ dae_type := DAEType.INNER;
638 end if;
639
640 ✗ if dae_type == DAEType.INNER then
641 ✗ for eqn_slice in eqn_slices loop
642 ✗ eqn := Slice.getT(eqn_slice);
643 ✗ eqn_name := Equation.getEqnName(eqn);
644 ✗ UnorderedSet.add(eqn_name, slice_set);
645 end for;
646 new_residuals := {};
647 else
648 ✗ new_residuals := List.flatten(acc_new_residuals);
649 end if;
650 end slicedDAEModeComponent;
651
652 function singleDAEModeComponent
653 input Pointer<Equation> eqn_ptr;
654 input VariablePointers variables;
655 input Pointer<Integer> uniqueIndex;
656 output list<StrongComponent> new_residuals;
657 output DAEType dae_type = DAEType.RESIDUAL;
658 protected
659 Pointer<list<Pointer<Equation>>> new_eqns;
660 UnorderedSet<VariablePointer> dummy_set;
661 Equation eqn;
662 list<Pointer<Equation>> eqns;
663 algorithm
664 1 new_eqns := Pointer.create({});
665 1 dummy_set := UnorderedSet.new(BVariable.hash, BVariable.equalName);
666 1 eqn := Inline.inlineRecordTupleArrayEquation(Pointer.access(eqn_ptr), Iterator.EMPTY(), variables, new_eqns, dummy_set, uniqueIndex, true);
667 1 eqns := Pointer.access(new_eqns);
668 // create equation, deliberately use new pointer. allow creating residual to fail and add original strong component to inners
669
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 eqns := if listEmpty(eqns) then {Pointer.create(eqn)} else eqns;
670 1 (new_residuals, dae_type) := inlinedDAEModeComponent(eqns);
671 end singleDAEModeComponent;
672
673 function inlinedDAEModeComponent
674 input list<Pointer<Equation>> eqns;
675 output list<StrongComponent> comps = {};
676 output DAEType dae_type = DAEType.UNPROCESSED;
677 protected
678 Pointer<Equation> new_eqn;
679 StrongComponent new_comp;
680 algorithm
681
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 for eqn in eqns loop
682
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if Equation.isDiscrete(eqn) then
683 // all sub equations are discrete, remove whole equation
684 if dae_type < DAEType.INNER then
685 dae_type := DAEType.REMOVED;
686 end if;
687 else
688 1 new_eqn := Equation.createResidual(eqn, NONE(), false, true);
689
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 if Equation.isResidual(new_eqn) then
690 // add to residuals
691 1 new_comp := SINGLE_COMPONENT(Equation.getResidualVar(new_eqn), new_eqn, NBSolve.Status.UNPROCESSED);
692 comps := new_comp :: comps;
693 dae_type := DAEType.RESIDUAL;
694 else
695 // cannot make residuals for all, make whole equation inner for now
696 // might not be neccessary --> needs further solving to get proper sub strong component
697 dae_type := DAEType.INNER; break;
698 end if;
699 end if;
700 end for;
701 end inlinedDAEModeComponent;
702
703 function fromSolvedEquationSlice
704 "creates a strong component assuming the equation is already solved
705 todo: if and when equations"
706 input Slice<EquationPointer> eqn_slice;
707 output StrongComponent comp;
708 protected
709 Pointer<Equation> eqn_ptr = Slice.getT(eqn_slice);
710 Equation eqn = Pointer.access(eqn_ptr);
711 IfEquationBody body;
712 list<Subscript> subs;
713 ComponentRef lhs_cref;
714 function simpleSolvedEquation
715 input Equation eqn;
716 input Pointer<Equation> eqn_ptr;
717 output StrongComponent comp;
718 algorithm
719 comp := match Equation.getLHS(eqn)
720 local
721 Expression lhs;
722 Pointer<Variable> var_ptr;
723 case SOME(lhs as Expression.CREF()) algorithm
724 1810 var_ptr := BVariable.getVarPointer(lhs.cref, sourceInfo());
725 // an element of an array variable keeps its subscripts, e.g. p[2] in an if-equation branch
726
3/4
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 1734 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 76 times.
1810 comp := if BVariable.isArray(var_ptr) and not Type.isArray(Expression.typeOf(lhs))
727 then SLICED_COMPONENT(lhs.cref, Slice.SLICE(var_ptr, {}), Slice.SLICE(eqn_ptr, {}), NBSolve.Status.EXPLICIT)
728 else SINGLE_COMPONENT(var_ptr, eqn_ptr, NBSolve.Status.EXPLICIT);
729 then comp;
730 ✗ else MULTI_COMPONENT(Equation.getLHSVars(eqn), Slice.SLICE(eqn_ptr, {}), NBSolve.Status.EXPLICIT);
731 end match;
732 end simpleSolvedEquation;
733 algorithm
734 // a genuine partial slice of an array equation (e.g. one row of a torn
735 // matrix-shaped subsystem, see NBTearing.scalarSlices) needs a
736 // SLICED_COMPONENT, not a SINGLE_COMPONENT -- the latter's .eqn field is a
737 // whole Pointer<Equation> with no room for which row this is, so every
738 // slice of the same array equation previously collapsed to an identical
739 // whole-array component, undercounting rows for the caller's adjacency/
740 // sparsity build (e.g. NBJacobian.compJacobian, empty Jacobian columns).
741
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1839 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1839 if not listEmpty(eqn_slice.indices) and Equation.isArrayEquation(eqn_ptr) and List.hasOneElement(eqn_slice.indices) then
742 ✗ subs := list(Subscript.INDEX(Expression.INTEGER(l + 1)) for l in Slice.indexToLocation(listHead(eqn_slice.indices), Equation.sizes(eqn_ptr)));
743 ✗ lhs_cref := ComponentRef.setSubscripts(subs, Expression.toCref(Util.getOption(Equation.getLHS(eqn))));
744 ✗ comp := SLICED_COMPONENT(lhs_cref, Slice.SLICE(BVariable.getVarPointer(lhs_cref, sourceInfo()), {}), eqn_slice, NBSolve.Status.EXPLICIT);
745 else
746 comp := match eqn
747 1723 case Equation.SCALAR_EQUATION() then simpleSolvedEquation(eqn, eqn_ptr);
748 76 case Equation.ARRAY_EQUATION() then simpleSolvedEquation(eqn, eqn_ptr);
749 11 case Equation.RECORD_EQUATION() then simpleSolvedEquation(eqn, eqn_ptr);
750 case Equation.IF_EQUATION(body = body) algorithm
751 ✗ if IfEquationBody.isSplit(body) then
752 ✗ comp := SINGLE_COMPONENT(BVariable.getVarPointer(Expression.toCref(Util.getOption(Equation.getLHS(eqn))), sourceInfo()), eqn_ptr, NBSolve.Status.EXPLICIT);
753 else
754 ✗ comp := MULTI_COMPONENT(Equation.getLHSVars(eqn), Slice.SLICE(eqn_ptr, {}), NBSolve.Status.EXPLICIT);
755 end if;
756 then comp;
757 29 case Equation.FOR_EQUATION() then SLICED_COMPONENT(ComponentRef.EMPTY(), Slice.SLICE(Pointer.create(NBVariable.DUMMY_VARIABLE), {}), eqn_slice, NBSolve.Status.EXPLICIT);
758 // ToDo: the other types
759 else algorithm
760 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for:\n" + Slice.toString(eqn_slice, function Equation.pointerToString(str = ""))});
761 ✗ then fail();
762 end match;
763 end if;
764 end fromSolvedEquationSlice;
765
766 function toSolvedEquation
767 "creates a solved equation for an explicitly solved strong component.
768 fails if it is not solved explicitly."
769 input StrongComponent comp;
770 output Pointer<Equation> eqn;
771 algorithm
772 eqn := match comp
773 ✗ case SINGLE_COMPONENT(status = NBSolve.Status.EXPLICIT) then comp.eqn;
774 ✗ case MULTI_COMPONENT(status = NBSolve.Status.EXPLICIT) then Slice.getT(comp.eqn);
775 6 case SLICED_COMPONENT(status = NBSolve.Status.EXPLICIT) then Slice.getT(comp.eqn);
776 ✗ case GENERIC_COMPONENT() then Slice.getT(comp.eqn);
777 else algorithm
778 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because strong component could not be
779 solved explicitly:\n" + toString(comp)});
780 ✗ then fail();
781 end match;
782 end toSolvedEquation;
783
784 function collectCrefs
785 "Collects dependent crefs in current comp and saves them in the
786 unordered map. Saves both directions."
787 input StrongComponent comp "strong component to be analyzed";
788 input VariablePointers var_rep "scalarized variable representatives";
789 input VariablePointers eqn_rep "scalarized equation representatives";
790 input Mapping var_rep_mapping "index mapping for variable representatives";
791 input Mapping eqn_rep_mapping "index mapping for equation representatives";
792 input UnorderedMap<ComponentRef, list<ComponentRef>> map "unordered map to save the dependencies";
793 input UnorderedSet<ComponentRef> set "unordered set of array crefs to check for relevance (index lookup)";
794 input JacobianType jacType "sets the context";
795 algorithm
796 () := match comp
797 local
798 Pointer<Equation> eqn_ptr;
799 ComponentRef cref;
800 list<ComponentRef> dependencies, loop_vars, tmp;
801 list<tuple<ComponentRef, list<ComponentRef>>> scalarized_dependencies;
802 Tearing strict;
803 Equation eqn, body;
804 Iterator iter;
805 UnorderedSet<ComponentRef> deps_set;
806
807 // sliced array equations - create all the single entries
808 case SINGLE_COMPONENT() guard(Equation.isArrayEquation(comp.eqn)) algorithm
809 ✗ dependencies := Equation.collectCrefs(Pointer.access(comp.eqn), function Slice.getDependentCrefCausalized(set = set), Expression.fakeMap);
810 ✗ scalarized_dependencies := Slice.getDependentCrefsPseudoArrayCausalized(BVariable.getVarName(comp.var), dependencies);
811 ✗ addScalarizedDependencies(scalarized_dependencies, map, jacType);
812 then ();
813
814 case SINGLE_COMPONENT() algorithm
815 ✗ dependencies := Equation.collectCrefs(Pointer.access(comp.eqn), function Slice.getDependentCrefCausalized(set = set), Expression.fakeMap);
816 ✗ dependencies := List.flatten(list(ComponentRef.scalarizeAll(dep, true) for dep in dependencies));
817 ✗ deps_set := prepareDependencies(UnorderedSet.fromList(dependencies, ComponentRef.hash, ComponentRef.isEqual), map, jacType);
818 ✗ updateDependencyMap(BVariable.getVarName(comp.var), deps_set, map);
819 then ();
820
821 case MULTI_COMPONENT() algorithm
822 ✗ dependencies := Equation.collectCrefs(Pointer.access(Slice.getT(comp.eqn)), function Slice.getDependentCrefCausalized(set = set), Expression.fakeMap);
823 ✗ dependencies := list(ComponentRef.stripIteratorSubscripts(dep) for dep in dependencies);
824 ✗ dependencies := List.flatten(list(ComponentRef.scalarizeAll(dep, true) for dep in dependencies));
825 ✗ deps_set := prepareDependencies(UnorderedSet.fromList(dependencies, ComponentRef.hash, ComponentRef.isEqual), map, jacType);
826 ✗ for var in comp.vars loop
827 ✗ for cref in ComponentRef.scalarizeAll(BVariable.getVarName(Slice.getT(var)), true) loop
828 ✗ updateDependencyMap(cref, deps_set, map);
829 end for;
830 end for;
831 then ();
832
833 // resizable for equations - create all the single entries
834 case RESIZABLE_COMPONENT() guard(Equation.isForEquation(Slice.getT(comp.eqn))) algorithm
835 ✗ addForLoopDependencies(Pointer.access(Slice.getT(comp.eqn)), comp.eqn.indices, comp.var_cref, var_rep, eqn_rep, var_rep_mapping, eqn_rep_mapping, map, set, jacType);
836 then ();
837
838 // sliced for equations - create all the single entries
839 case SLICED_COMPONENT() guard(Equation.isForEquation(Slice.getT(comp.eqn))) algorithm
840 ✗ addForLoopDependencies(Pointer.access(Slice.getT(comp.eqn)), comp.eqn.indices, comp.var_cref, var_rep, eqn_rep, var_rep_mapping, eqn_rep_mapping, map, set, jacType);
841 then ();
842
843 // sliced array equations - create all the single entries
844 case SLICED_COMPONENT() guard(Equation.isArrayEquation(Slice.getT(comp.eqn))) algorithm
845 ✗ eqn := Pointer.access(Slice.getT(comp.eqn));
846 ✗ dependencies := Equation.collectCrefs(eqn, function Slice.getDependentCrefCausalized(set = set), Expression.fakeMap);
847 ✗ scalarized_dependencies := Slice.getDependentCrefsPseudoArrayCausalized(comp.var_cref, dependencies, comp.eqn.indices);
848 ✗ addScalarizedDependencies(scalarized_dependencies, map, jacType);
849 then ();
850
851 // sliced regular equation.
852 case SLICED_COMPONENT() algorithm
853 ✗ eqn := Pointer.access(Slice.getT(comp.eqn));
854 ✗ dependencies := Equation.collectCrefs(eqn, function Slice.getDependentCrefCausalized(set = set), Expression.fakeMap);
855 ✗ dependencies := List.flatten(list(ComponentRef.scalarizeAll(dep, true) for dep in dependencies));
856 ✗ deps_set := prepareDependencies(UnorderedSet.fromList(dependencies, ComponentRef.hash, ComponentRef.isEqual), map, jacType);
857 ✗ updateDependencyMap(comp.var_cref, deps_set, map);
858 then ();
859
860 // sliced for equations - create all the single entries
861 case GENERIC_COMPONENT() guard(Equation.isForEquation(Slice.getT(comp.eqn))) algorithm
862 ✗ addForLoopDependencies(Pointer.access(Slice.getT(comp.eqn)), comp.eqn.indices, comp.var_cref, var_rep, eqn_rep, var_rep_mapping, eqn_rep_mapping, map, set, jacType);
863 then ();
864
865 case ALGEBRAIC_LOOP(strict = strict) algorithm
866 // traverse residual equations and collect dependencies
867 ✗ deps_set := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
868 ✗ for slice in strict.residual_eqns loop
869 // ToDo: does this work properly for arrays?
870 ✗ tmp := Equation.collectCrefs(Pointer.access(Slice.getT(slice)), function Slice.getDependentCrefCausalized(set = set), Expression.fakeMap);
871 ✗ eqn_ptr := Slice.getT(slice);
872 ✗ if Equation.isForEquation(eqn_ptr) then
873 // if its a for equation get all dependencies corresponding to their residual.
874 // we do not really care for order and assume full dependency anyway
875 ✗ Equation.FOR_EQUATION(iter = iter, body = {body}) := Pointer.access(eqn_ptr);
876 ✗ cref := Equation.getEqnName(eqn_ptr);
877 ✗ scalarized_dependencies := Slice.getDependentCrefsPseudoForCausalized(
878 cref, tmp, var_rep, eqn_rep, var_rep_mapping, eqn_rep_mapping,
879 iter, Equation.size(eqn_ptr), slice.indices, true);
880 ✗ tmp := List.flatten(list(Util.tuple22(tpl) for tpl in scalarized_dependencies));
881 end if;
882 ✗ for dep in tmp loop
883 ✗ for scal in ComponentRef.scalarizeAll(dep, true) loop
884 ✗ UnorderedSet.add(scal, deps_set);
885 end for;
886 end for;
887 end for;
888 ✗ deps_set := prepareDependencies(deps_set, map, jacType);
889
890 // collect iteration loop vars
891 ✗ loop_vars := list(BVariable.getVarName(Slice.getT(var)) for var in strict.iteration_vars);
892
893 // traverse inner equations and collect loop vars and dependencies
894 ✗ for i in 1:arrayLength(strict.innerEquations) loop
895 // collect inner equation dependencies
896 ✗ collectCrefs(strict.innerEquations[i], var_rep, eqn_rep, var_rep_mapping, eqn_rep_mapping, map, set, jacType);
897
898 // collect inner loop variables
899 ✗ loop_vars := listAppend(list(BVariable.getVarName(var) for var in getVariables(strict.innerEquations[i])), loop_vars);
900 end for;
901
902 // add all dependencies
903 ✗ for cref in loop_vars loop
904 ✗ updateDependencyMap(cref, deps_set, map);
905 end for;
906
907 then ();
908
909 case ALIAS() algorithm
910 ✗ collectCrefs(comp.original, var_rep, eqn_rep, var_rep_mapping, eqn_rep_mapping, map, set, jacType);
911 then ();
912
913 /* ToDo add the others and let else case fail! */
914
915 else ();
916 end match;
917 end collectCrefs;
918
919 function addScalarizedDependencies
920 input list<tuple<ComponentRef, list<ComponentRef>>> scalarized_dependencies;
921 input UnorderedMap<ComponentRef, list<ComponentRef>> map "unordered map to save the dependencies";
922 input JacobianType jacType "sets the context";
923 protected
924 ComponentRef cref;
925 list<ComponentRef> dependencies;
926 UnorderedSet<ComponentRef> deps_set;
927 algorithm
928 ✗ for tpl in listReverse(scalarized_dependencies) loop
929 ✗ (cref, dependencies) := tpl;
930 ✗ deps_set := prepareDependencies(UnorderedSet.fromList(dependencies, ComponentRef.hash, ComponentRef.isEqual), map, jacType);
931 ✗ updateDependencyMap(cref, deps_set, map);
932 end for;
933 end addScalarizedDependencies;
934
935 function addForLoopDependencies
936 input Equation eqn;
937 input list<Integer> indices;
938 input ComponentRef var_cref;
939 input VariablePointers var_rep "scalarized variable representatives";
940 input VariablePointers eqn_rep "scalarized equation representatives";
941 input Mapping var_rep_mapping "index mapping for variable representatives";
942 input Mapping eqn_rep_mapping "index mapping for equation representatives";
943 input UnorderedMap<ComponentRef, list<ComponentRef>> map "unordered map to save the dependencies";
944 input UnorderedSet<ComponentRef> set "unordered set of array crefs to check for relevance (index lookup)";
945 input JacobianType jacType "sets the context";
946 protected
947 Iterator iter;
948 Equation body;
949 list<ComponentRef> dependencies;
950 ComponentRef cref;
951 list<tuple<ComponentRef, list<ComponentRef>>> scalarized_dependencies;
952 algorithm
953 try
954 ✗ Equation.FOR_EQUATION(iter = iter, body = {body}) := eqn;
955 else
956 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because the for-loop had more than one body equation:\n" + Equation.toString(eqn)});
957 ✗ fail();
958 end try;
959
960 ✗ dependencies := Equation.collectCrefs(eqn, function Slice.getDependentCrefCausalized(set = set), Expression.fakeMap);
961 ✗ if ComponentRef.isEmpty(var_cref) then
962 ✗ SOME(Expression.CREF(cref = cref)) := Equation.getLHS(body);
963 else
964 cref := var_cref;
965 end if;
966 ✗ scalarized_dependencies := Slice.getDependentCrefsPseudoForCausalized(
967 cref, dependencies, var_rep, eqn_rep, var_rep_mapping, eqn_rep_mapping,
968 iter, Equation.size(Pointer.create(eqn)), indices, false);
969 ✗ addScalarizedDependencies(scalarized_dependencies, map, jacType);
970 end addForLoopDependencies;
971
972 function addLoopJacobian
973 input output StrongComponent comp;
974 input Option<BackendDAE> jac;
975 algorithm
976 comp := match comp
977 local
978 Tearing strict;
979
980 case ALGEBRAIC_LOOP(strict = strict) algorithm
981 // ToDo: update linearity here
982 ✗ strict.jac := jac;
983 ✗ comp.strict := strict;
984 then comp;
985
986 else algorithm
987 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because of wrong component: " + toString(comp)});
988 ✗ then fail();
989 end match;
990 end addLoopJacobian;
991
992 function getLoopResiduals
993 input StrongComponent comp;
994 output list<Pointer<Variable>> residuals;
995 algorithm
996 residuals := match comp
997 80 case ALGEBRAIC_LOOP() then Tearing.getResidualVars(comp.strict);
998 else {};
999 end match;
1000 end getLoopResiduals;
1001
1002 function getVariables
1003 input StrongComponent comp;
1004 output list<Pointer<Variable>> vars;
1005 algorithm
1006 vars := match comp
1007 1278 case SINGLE_COMPONENT() then {comp.var};
1008
4/4
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 9 times.
27 case MULTI_COMPONENT() then list(Slice.getT(v) for v in comp.vars);
1009 217 case SLICED_COMPONENT() then {Slice.getT(comp.var)};
1010 31 case RESIZABLE_COMPONENT() then {Slice.getT(comp.var)};
1011 40 case GENERIC_COMPONENT() then {Slice.getT(comp.var)};
1012 ✗ case ENTWINED_COMPONENT() then List.flatten(list(getVariables(slice) for slice in comp.entwined_slices));
1013 ✗ case ALGEBRAIC_LOOP() then Tearing.getVariables(comp.strict);
1014 ✗ case ALIAS() then getVariables(comp.original);
1015 else algorithm
1016 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because of wrong component: " + toString(comp)});
1017 ✗ then fail();
1018 end match;
1019 end getVariables;
1020
1021 function getVariableCrefs
1022 input StrongComponent comp;
1023 output list<ComponentRef> var_crefs;
1024 algorithm
1025 var_crefs := match comp
1026 1168 case SINGLE_COMPONENT() then {BVariable.getVarName(comp.var)};
1027
4/4
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 11 times.
33 case MULTI_COMPONENT() then list(BVariable.getVarName(Slice.getT(v)) for v in comp.vars);
1028 case SLICED_COMPONENT() algorithm
1029 // EMPTY var_cref (NLS FOR residuals): use residual var for pder classification in fullToSparsity
1030
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 307 times.
321 if ComponentRef.isEmpty(comp.var_cref) then
1031 try
1032 14 var_crefs := {BVariable.getVarName(Equation.getResidualVar(Slice.getT(comp.eqn)))};
1033 else
1034 ✗ var_crefs := {comp.var_cref};
1035 end try;
1036 else
1037 307 var_crefs := {comp.var_cref};
1038 end if;
1039 then var_crefs;
1040 139 case RESIZABLE_COMPONENT() then {comp.var_cref};
1041 46 case GENERIC_COMPONENT() then {comp.var_cref};
1042 ✗ case ENTWINED_COMPONENT() then List.flatten(list(list(BVariable.getVarName(var) for var in getVariables(slice)) for slice in comp.entwined_slices));
1043
4/4
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 58 times.
✓ Branch 4 taken 16 times.
74 case ALGEBRAIC_LOOP() then list(BVariable.getVarName(var) for var in Tearing.getVariables(comp.strict));
1044 521 case ALIAS() then getVariableCrefs(comp.original);
1045 else algorithm
1046 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because of wrong component: " + toString(comp)});
1047 ✗ then fail();
1048 end match;
1049 end getVariableCrefs;
1050
1051 function getVarCref
1052 input StrongComponent comp;
1053 output ComponentRef var_cref;
1054 algorithm
1055 var_cref := match comp
1056 ✗ case SLICED_COMPONENT() then comp.var_cref;
1057 ✗ case RESIZABLE_COMPONENT() then comp.var_cref;
1058 ✗ case GENERIC_COMPONENT() then comp.var_cref;
1059 ✗ case ALIAS() then getVarCref(comp.original);
1060 else algorithm
1061 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because of wrong component: " + toString(comp)});
1062 ✗ then fail();
1063 end match;
1064 end getVarCref;
1065
1066 function getEquations
1067 input StrongComponent comp;
1068 output list<Pointer<Equation>> eqns;
1069 algorithm
1070 eqns := match comp
1071 2397 case SINGLE_COMPONENT() then {comp.eqn};
1072 28 case MULTI_COMPONENT() then {Slice.getT(comp.eqn)};
1073 644 case SLICED_COMPONENT() then {Slice.getT(comp.eqn)};
1074 281 case RESIZABLE_COMPONENT() then {Slice.getT(comp.eqn)};
1075 92 case GENERIC_COMPONENT() then {Slice.getT(comp.eqn)};
1076 ✗ case ENTWINED_COMPONENT() then List.flatten(list(getEquations(slice) for slice in comp.entwined_slices));
1077 32 case ALGEBRAIC_LOOP() then Tearing.getResidualEqns(comp.strict); // + inner?
1078 1042 case ALIAS() then getEquations(comp.original);
1079 else algorithm
1080 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because of wrong component: " + toString(comp)});
1081 ✗ then fail();
1082 end match;
1083 end getEquations;
1084
1085 function getSolveStatus
1086 input StrongComponent comp;
1087 output Solve.Status status;
1088 algorithm
1089 status := match comp
1090 138 case SINGLE_COMPONENT() then comp.status;
1091 3 case MULTI_COMPONENT() then comp.status;
1092 206 case SLICED_COMPONENT() then comp.status;
1093 25 case RESIZABLE_COMPONENT() then comp.status;
1094 case GENERIC_COMPONENT() then NBSolve.Status.EXPLICIT;
1095 case ENTWINED_COMPONENT() then NBSolve.Status.EXPLICIT;
1096 ✗ case ALGEBRAIC_LOOP() then comp.status;
1097 ✗ case ALIAS() then getSolveStatus(comp.original);
1098 else algorithm
1099 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because of wrong component: " + toString(comp)});
1100 ✗ then fail();
1101 end match;
1102 end getSolveStatus;
1103
1104 function isDiscrete
1105 "checks if all equations are discrete"
1106 input StrongComponent comp;
1107 output Boolean b;
1108 algorithm
1109 b := match comp
1110 2413 case SINGLE_COMPONENT() then Equation.isDiscrete(comp.eqn);
1111 // mixed algorithms assign both discrete (e.g. $SEV_0) and continuous vars;
1112 // treat as discrete so Jacobian computation does not try to differentiate boolean assignments
1113
8/8
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 58 times.
✓ Branch 3 taken 24 times.
✓ Branch 7 taken 22 times.
✓ Branch 8 taken 2 times.
✓ Branch 10 taken 7 times.
✓ Branch 11 taken 15 times.
82 case MULTI_COMPONENT() then Equation.isDiscrete(Slice.getT(comp.eqn)) or
1114 List.any(list(Slice.getT(v) for v in comp.vars), BVariable.isDiscrete);
1115 1087 case SLICED_COMPONENT() then Equation.isDiscrete(Slice.getT(comp.eqn));
1116 247 case RESIZABLE_COMPONENT() then Equation.isDiscrete(Slice.getT(comp.eqn));
1117 ✗ case ENTWINED_COMPONENT() then List.all(comp.entwined_slices, isDiscrete);
1118 47 case GENERIC_COMPONENT() then Equation.isDiscrete(Slice.getT(comp.eqn));
1119 29 case ALGEBRAIC_LOOP() then comp.mixed;
1120 2719 case ALIAS() then isDiscrete(comp.original);
1121 else algorithm
1122 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because of wrong component: " + toString(comp)});
1123 ✗ then fail();
1124 end match;
1125 end isDiscrete;
1126
1127 function isDummy
1128 input StrongComponent comp;
1129 output Boolean b;
1130 algorithm
1131 b := match comp
1132 2740 case SINGLE_COMPONENT() then Equation.isDummy(Pointer.access(comp.eqn));
1133 140 case MULTI_COMPONENT() then Equation.isDummy(Pointer.access(Slice.getT(comp.eqn)));
1134 else false;
1135 end match;
1136 end isDummy;
1137
1138 function isAlias
1139 input StrongComponent comp;
1140 output Boolean b;
1141 algorithm
1142 b := match comp
1143 case ALIAS() then true;
1144 else false;
1145 end match;
1146 end isAlias;
1147
1148 function isSingleComponent
1149 input StrongComponent comp;
1150 output Boolean b;
1151 algorithm
1152 b := match removeAlias(comp)
1153 case SINGLE_COMPONENT() then true;
1154 else false;
1155 end match;
1156 end isSingleComponent;
1157
1158 function isAlgebraicLoop
1159 input StrongComponent comp;
1160 output Boolean b;
1161 algorithm
1162 b := match removeAlias(comp)
1163 case ALGEBRAIC_LOOP() then true;
1164 else false;
1165 end match;
1166 end isAlgebraicLoop;
1167
1168 function setHomotopy
1169 input output StrongComponent comp;
1170 input Boolean homotopy;
1171 algorithm
1172 comp := match comp
1173 case ALGEBRAIC_LOOP() algorithm
1174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 comp.homotopy := homotopy;
1175 then comp;
1176 else comp;
1177 end match;
1178 end setHomotopy;
1179
1180 function createPseudoScalar
1181 input list<Integer> comp_indices;
1182 input array<Integer> eqn_to_var;
1183 input Adjacency.Mapping mapping;
1184 input VariablePointers vars;
1185 input EquationPointers eqns;
1186 output StrongComponent comp;
1187 algorithm
1188 comp := match comp_indices
1189 local
1190 Integer i, var_scal_idx, var_arr_idx;
1191 Pointer<Variable> var;
1192 Pointer<Equation> eqn;
1193 list<Slice<VariablePointer>> comp_vars;
1194 list<Slice<EquationPointer>> comp_eqns;
1195 Tearing tearingSet;
1196 Slice<VariablePointer> var_slice;
1197 Slice<EquationPointer> eqn_slice;
1198 Pointer<Boolean> homotopy = Pointer.create(false);
1199 ComponentRef resolved_cref;
1200
1201 // Size 1 strong component
1202 // - case 1: sliced equation because of for-equation
1203 // - case 2: multi components for when/if and algorithm although its size 1
1204 // - case 3: single or sliced strong component
1205 case {i} algorithm
1206 7044 var_scal_idx := eqn_to_var[i];
1207 7044 var_arr_idx := mapping.var_StA[var_scal_idx];
1208 7044 var := VariablePointers.getVarAt(vars, var_arr_idx);
1209 7044 eqn := EquationPointers.getEqnAt(eqns, mapping.eqn_StA[i]);
1210
1211
2/2
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 6986 times.
7044 if Equation.isForEquation(eqn) then
1212 // - case 1: sliced equation because of for-equation
1213 try
1214
4/8
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 58 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 58 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 58 times.
58 ({var_slice}, {eqn_slice}) := getLoopVarsAndEqns(comp_indices, eqn_to_var, mapping, vars, eqns);
1215 else
1216 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because single indices did not turn out to be single components."});
1217 ✗ fail();
1218 end try;
1219 58 comp := SLICED_COMPONENT(VariablePointers.varSlice(vars, var_scal_idx, mapping.var_StA[var_scal_idx], mapping, true), var_slice, eqn_slice, NBSolve.Status.UNPROCESSED);
1220 elseif Equation.isCompound(eqn) then
1221 // - case 2: multi components for when/if and algorithm although its size 1
1222 138 comp := MULTI_COMPONENT({Slice.SLICE(var, {})}, Slice.SLICE(eqn, {}), NBSolve.Status.UNPROCESSED);
1223 else
1224 // - case 3: single or sliced strong component
1225 try
1226
4/8
✗ Branch 1 not taken.
✓ Branch 2 taken 6917 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6917 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6917 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6917 times.
6917 ({var_slice}, {eqn_slice}) := getLoopVarsAndEqns(comp_indices, eqn_to_var, mapping, vars, eqns);
1227 else
1228 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because single indices did not turn out to be single components."});
1229 ✗ fail();
1230 end try;
1231 6917 comp := createSliceOrSingle(VariablePointers.varSlice(vars, var_scal_idx, mapping.var_StA[var_scal_idx], mapping, true), var_slice, eqn_slice);
1232 end if;
1233 then comp;
1234
1235 // Size > 1 strong component
1236 case _ algorithm
1237 234 (comp_vars, comp_eqns) := getLoopVarsAndEqns(comp_indices, eqn_to_var, mapping, vars, eqns);
1238 comp := match (comp_vars, comp_eqns)
1239 case ({var_slice}, {eqn_slice}) guard(not (Equation.isForEquation(Slice.getT(eqn_slice)) or Equation.isAlgorithm(Slice.getT(eqn_slice))))
1240 algorithm
1241 // var_slice can be a genuine partial slice (e.g. i_s[{1, 2}]); the bare
1242 // declared name loses that, so resolve the cref that actually occurs in the
1243 // equation with the matching size instead (SLICED_COMPONENT.var_cref is
1244 // documented to carry subscripts, see NBSolve.solveStrongComponent).
1245
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 resolved_cref := if Slice.isFull(var_slice) then BVariable.getVarName(Slice.getT(var_slice))
1246 else Slice.resolveSlicedCref(BVariable.getVarName(Slice.getT(var_slice)), Pointer.access(Slice.getT(eqn_slice)), Slice.size(var_slice, function BVariable.size(resize = false)));
1247 46 then createSliceOrSingle(resolved_cref, var_slice, eqn_slice);
1248
1249 // for equations that are not algebraic loops are caught earlier! Any for equation
1250 // getting to this point is an actual algebraic loop, unless it is a tuple that
1251 // assigns a variable to each of its outputs in every iteration
1252 case (_, {eqn_slice}) guard(not Equation.isForEquation(Slice.getT(eqn_slice)) or Equation.isRecordOrTupleEquation(Slice.getT(eqn_slice)))
1253 92 then MULTI_COMPONENT(
1254 vars = comp_vars,
1255 eqn = eqn_slice,
1256 status = NBSolve.Status.UNPROCESSED
1257 );
1258
1259 else algorithm
1260 96 tearingSet := Tearing.TEARING_SET(
1261 iteration_vars = comp_vars,
1262 residual_eqns = comp_eqns,
1263 innerEquations = listArray({}),
1264 jac = NONE());
1265
2/2
✓ Branch 0 taken 1659 times.
✓ Branch 1 taken 96 times.
1755 for eqn in comp_eqns loop
1266 1659 Equation.map(Pointer.access(Slice.getT(eqn)), function Initialization.containsHomotopyCall(b = homotopy));
1267 end for;
1268 96 then ALGEBRAIC_LOOP(
1269 idx = -1,
1270 strict = tearingSet,
1271 casual = NONE(),
1272 linear = false,
1273 mixed = false,
1274 homotopy = Pointer.access(homotopy),
1275 status = NBSolve.Status.IMPLICIT,
1276 implicitlyCreated = false);
1277 end match;
1278 then comp;
1279
1280 else algorithm
1281 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."});
1282 ✗ then fail();
1283 end match;
1284 end createPseudoScalar;
1285
1286 function createSliceOrSingle
1287 input ComponentRef cref;
1288 input Slice<VariablePointer> var_slice;
1289 input Slice<EquationPointer> eqn_slice;
1290 output StrongComponent comp;
1291 algorithm
1292
5/6
✓ Branch 1 taken 5704 times.
✓ Branch 2 taken 1855 times.
✓ Branch 4 taken 5704 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5364 times.
✓ Branch 8 taken 340 times.
7559 if Slice.isFull(var_slice) and Slice.isFull(eqn_slice) and not ComponentRef.hasSubscripts(cref) then
1293 5364 comp := SINGLE_COMPONENT(
1294 var = Slice.getT(var_slice),
1295 eqn = Slice.getT(eqn_slice),
1296 status = NBSolve.Status.UNPROCESSED);
1297 else
1298 2195 comp := SLICED_COMPONENT(
1299 var_cref = cref,
1300 var = var_slice,
1301 eqn = eqn_slice,
1302 status = NBSolve.Status.UNPROCESSED);
1303 end if;
1304 end createSliceOrSingle;
1305
1306 // ############################################################
1307 // Protected Functions and Types
1308 // ############################################################
1309
1310 protected
1311 function getLoopVarsAndEqns
1312 "adds the equation and matched variable to accumulated lists.
1313 used to collect algebraic loops.
1314 ToDo: currently assumes full dependency - update with Slice structures!"
1315 input list<Integer> comp_indices;
1316 input array<Integer> eqn_to_var;
1317 input Adjacency.Mapping mapping;
1318 input VariablePointers vars;
1319 input EquationPointers eqns;
1320 output list<Slice<VariablePointer>> acc_vars = {};
1321 output list<Slice<EquationPointer>> acc_eqns = {};
1322 protected
1323 Integer var_idx, var_arr_idx, var_scal_idx, eqn_arr_idx, eqn_scal_idx;
1324 list<Integer> idx_lst;
1325 Pointer<Variable> var;
1326 Pointer<Equation> eqn;
1327 Integer len_comps = listLength(comp_indices);
1328 UnorderedMap<Integer, Slice.IntLst> var_map = UnorderedMap.new<Slice.IntLst>(Util.id, intEq, len_comps);
1329 UnorderedMap<Integer, Slice.IntLst> eqn_map = UnorderedMap.new<Slice.IntLst>(Util.id, intEq, len_comps);
1330 algorithm
1331 // store all component var and eqn indices in maps
1332
2/2
✓ Branch 0 taken 9953 times.
✓ Branch 1 taken 7209 times.
17162 for eqn_idx in comp_indices loop
1333 9953 var_idx := eqn_to_var[eqn_idx];
1334 9953 var_arr_idx := mapping.var_StA[var_idx];
1335 9953 eqn_arr_idx := mapping.eqn_StA[eqn_idx];
1336
1337 // collect variable and equation slices
1338 9953 idx_lst := UnorderedMap.getOrDefault(var_arr_idx, var_map, {});
1339 9953 UnorderedMap.add(var_arr_idx, var_idx :: idx_lst, var_map);
1340 9953 idx_lst := UnorderedMap.getOrDefault(eqn_arr_idx, eqn_map, {});
1341 9953 UnorderedMap.add(eqn_arr_idx, eqn_idx :: idx_lst, eqn_map);
1342 end for;
1343
1344 // extract variables and equations from maps
1345 // check if slices are full and reduce them to base 0 indexing
1346 // sort the index lists so we can compare two equal loops, order doesn't matter for evaluating inside alg. loops
1347
2/2
✓ Branch 1 taken 8783 times.
✓ Branch 2 taken 7209 times.
15992 for tpl in UnorderedMap.toList(var_map) loop
1348 8783 (var_arr_idx, idx_lst) := tpl;
1349 8783 (var_scal_idx, _) := mapping.var_AtS[var_arr_idx];
1350 8783 var := VariablePointers.getVarAt(vars, var_arr_idx);
1351
6/6
✓ Branch 2 taken 1989 times.
✓ Branch 3 taken 6794 times.
✓ Branch 4 taken 2378 times.
✓ Branch 5 taken 1989 times.
✓ Branch 6 taken 2378 times.
✓ Branch 7 taken 1989 times.
11161 idx_lst := if listLength(idx_lst) == BVariable.size(var) then {} else list(i - var_scal_idx for i in idx_lst);
1352 8783 acc_vars := Slice.SLICE(var, sortAscending(idx_lst)) :: acc_vars;
1353 end for;
1354
2/2
✓ Branch 1 taken 8772 times.
✓ Branch 2 taken 7209 times.
15981 for tpl in UnorderedMap.toList(eqn_map) loop
1355 8772 (eqn_arr_idx, idx_lst) := tpl;
1356 8772 (eqn_scal_idx, _) := mapping.eqn_AtS[eqn_arr_idx];
1357 8772 eqn := EquationPointers.getEqnAt(eqns, eqn_arr_idx);
1358
6/6
✓ Branch 2 taken 200 times.
✓ Branch 3 taken 8572 times.
✓ Branch 4 taken 392 times.
✓ Branch 5 taken 200 times.
✓ Branch 6 taken 392 times.
✓ Branch 7 taken 200 times.
9164 idx_lst := if listLength(idx_lst) == Equation.size(eqn) then {} else list(i - eqn_scal_idx for i in idx_lst);
1359 8772 acc_eqns := Slice.SLICE(eqn, sortAscending(idx_lst)) :: acc_eqns;
1360 end for;
1361 end getLoopVarsAndEqns;
1362
1363 function sortAscending
1364 "List.sort(lst, intGt) without the merge sort's allocations"
1365 input list<Integer> lst;
1366 output list<Integer> sorted;
1367 algorithm
1368
4/4
✓ Branch 0 taken 2189 times.
✓ Branch 1 taken 15366 times.
✓ Branch 3 taken 153 times.
✓ Branch 4 taken 2036 times.
17555 if listEmpty(lst) or listEmpty(listRest(lst)) then
1369 sorted := lst;
1370 else
1371 153 sorted := arrayList(Array.heapSort(listArray(lst)));
1372 end if;
1373 end sortAscending;
1374
1375 function updateDependencyMap
1376 input ComponentRef cref "cref representing current equation";
1377 input UnorderedSet<ComponentRef> dependencies "the dependency crefs";
1378 input UnorderedMap<ComponentRef, list<ComponentRef>> map "unordered map to save the dependencies";
1379 protected
1380 Boolean removed;
1381 algorithm
1382 ✗ removed := UnorderedSet.remove(cref, dependencies) "remove self dependency";
1383 ✗ UnorderedMap.add(cref, UnorderedSet.toList(dependencies), map) "update the current value (res/tmp) --> {independent vars}";
1384 ✗ if removed then UnorderedSet.addNew(cref, dependencies); end if "restore dependencies";
1385 end updateDependencyMap;
1386
1387 function prepareDependencies
1388 input output UnorderedSet<ComponentRef> dependencies;
1389 input UnorderedMap<ComponentRef, list<ComponentRef>> map;
1390 input JacobianType jacType;
1391 protected
1392 function addSubDependencies
1393 input ComponentRef dep;
1394 input UnorderedMap<ComponentRef, list<ComponentRef>> map;
1395 input BVariable.checkVar checkFn;
1396 input output UnorderedSet<ComponentRef> set;
1397 algorithm
1398 // if the dependency is a state add itself, otherwise add the dependencies already saved
1399 // (those are known to be states). ToDo: avoid this check by adding state self dependency beforehand?
1400 ✗ if BVariable.checkCref(dep, checkFn, sourceInfo()) then
1401 ✗ UnorderedSet.add(dep, set);
1402 else
1403 ✗ for tmp in UnorderedMap.getSafe(dep, map, sourceInfo()) loop
1404 ✗ UnorderedSet.add(tmp, set);
1405 end for;
1406 end if;
1407 end addSubDependencies;
1408 algorithm
1409 ✗ dependencies := UnorderedSet.selfMap(dependencies, function ComponentRef.mapExp(func = Expression.replaceResizableParameter));
1410 ✗ dependencies := UnorderedSet.selfMap(dependencies, function ComponentRef.simplifySubscripts(trim = false));
1411 // replace non derivative dependencies with their previous dependencies
1412 // (be careful with algebraic loops. this here assumes that cyclic dependencies have already been resolved)
1413 dependencies := match jacType
1414 case NBJacobian.JacobianType.ODE
1415 ✗ then UnorderedSet.fold(dependencies, function addSubDependencies(map = map, checkFn = BVariable.isState), UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual));
1416 // TODO: for optimization these checks / checkFn are not valid yet, add free time
1417 case NBJacobian.JacobianType.OPT_LFG
1418 ✗ then UnorderedSet.fold(dependencies, function addSubDependencies(map = map, checkFn = BVariable.isStateOrOptimizable), UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual));
1419 case NBJacobian.JacobianType.OPT_MRF
1420 ✗ then UnorderedSet.fold(dependencies, function addSubDependencies(map = map, checkFn = BVariable.isStateOrOptimizable), UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual));
1421 case NBJacobian.JacobianType.OPT_R0
1422 ✗ then UnorderedSet.fold(dependencies, function addSubDependencies(map = map, checkFn = BVariable.isStateOrOptimizable), UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual));
1423 else dependencies;
1424 end match;
1425 end prepareDependencies;
1426
1427 annotation(__OpenModelica_Interface="nbackend");
1428 end NBStrongComponent;
1429