Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 89.1% 90 / 0 / 101
Functions: -% 0 / 1 / 1
Branches: 77.3% 51 / 0 / 66

OMCompiler/Compiler/NBackEnd/Modules/1_Main/NBCausalize.mo
Line Branch Exec Source
1 /*
2 * This file is part of OpenModelica.
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC),
5 * c/o Linköpings universitet, Department of Computer and Information Science,
6 * SE-58183 Linköping, Sweden.
7 *
8 * All rights reserved.
9 *
10 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR
11 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8.
12 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
13 * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL
14 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
15 *
16 * The OpenModelica software and the OSMC (Open Source Modelica Consortium)
17 * Public License (OSMC-PL) are obtained from OSMC, either from the above
18 * address, from the URLs:
19 * http://www.openmodelica.org or
20 * https://github.com/OpenModelica/ or
21 * http://www.ida.liu.se/projects/OpenModelica,
22 * and in the OpenModelica distribution.
23 *
24 * GNU AGPL version 3 is obtained from:
25 * https://www.gnu.org/licenses/licenses.html#GPL
26 *
27 * This program is distributed WITHOUT ANY WARRANTY; without
28 * even the implied warranty of MERCHANTABILITY or FITNESS
29 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
30 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
31 *
32 * See the full OSMC Public License conditions for more details.
33 *
34 */
35
36 encapsulated package NBCausalize
37 "file: NBCausalize.mo
38 package: NBCausalize
39 description: This file contains the functions which perform the causalization process;
40 "
41
42 public
43 import Module = NBModule;
44
45 protected
46 // OF imports
47 import Absyn.Path;
48
49 // NF imports
50 import ComponentRef = NFComponentRef;
51 import Dimension = NFDimension;
52 import Expression = NFExpression;
53 import NFFunction.Function;
54 import InstNode = NFInstNode.InstNode;
55 import Prefixes = NFPrefixes;
56 import Subscript = NFSubscript;
57 import Type = NFType;
58 import TypeCheck = NFTypeCheck;
59 import Variable = NFVariable;
60 import NFArrayConnections.NameVertexTable;
61
62 // Backend imports
63 import Adjacency = NBAdjacency;
64 import ASSC = NBASSC;
65 import BackendDAE = NBackendDAE;
66 import BEquation = NBEquation;
67 import Differentiate = NBDifferentiate;
68 import NBEquation.{Equation, EquationPointers, EqData, EquationAttributes, Iterator};
69 import Matching = NBMatching;
70 import Sorting = NBSorting;
71 import StrongComponent = NBStrongComponent;
72 import BPartition = NBPartition;
73 import NBPartition.Partition;
74 import BVariable = NBVariable;
75 import NBVariable.{VariablePointers, VarData};
76
77 // util imports
78 import BackendUtil = NBBackendUtil;
79 import Error;
80 import ErrorExt;
81 import List;
82 import StringUtil;
83
84 // ############################################################
85 // Main Functions
86 // ############################################################
87
88 public
89 function main extends Module.wrapper;
90 input BPartition.Kind kind;
91 protected
92 Module.causalizeInterface func = getModule();
93 algorithm
94 bdae := match (kind, bdae)
95 local
96 list<Partition> partitions, clocked, twins;
97 VarData varData;
98 EqData eqData;
99
100 case (NBPartition.Kind.ODE, BackendDAE.MAIN(ode = partitions, clocked = clocked, varData = varData, eqData = eqData))
101 algorithm
102 190 (partitions, varData, eqData) := applyModule(partitions, kind, varData, eqData, bdae.funcMap, func);
103 188 (clocked, varData, eqData) := applyModule(clocked, kind, varData, eqData, bdae.funcMap, func);
104 188 bdae.ode := partitions;
105 188 bdae.clocked := clocked;
106 188 bdae.varData := varData;
107 188 bdae.eqData := eqData;
108 then bdae;
109
110 case (_, BackendDAE.MAIN(init = partitions, varData = varData, eqData = eqData)) guard(Partition.kindIsInitial(kind))
111 algorithm
112
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 188 times.
188 if Flags.isSet(Flags.INITIALIZATION) then
113 ✗ print(StringUtil.headline_1("Balance Initialization") + "\n");
114 end if;
115 // init_0 is init with the homotopy calls replaced by their simplified branch
116 188 (partitions, varData, eqData, twins) := applyModule(partitions, kind, varData, eqData, bdae.funcMap, func, Util.getOptionOrDefault(bdae.init_0, {}));
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
188 bdae.init := partitions;
118
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
119 6 bdae.init_0 := SOME(twins);
120 end if;
121 188 bdae.varData := varData;
122 188 bdae.eqData := eqData;
123 then bdae;
124
125 case (NBPartition.Kind.DAE, BackendDAE.MAIN(dae = SOME(partitions), varData = varData, eqData = eqData))
126 algorithm
127 1 (partitions, varData, eqData) := applyModule(partitions, kind, varData, eqData, bdae.funcMap, causalizeDAEMode);
128 1 bdae.dae := SOME(partitions);
129 1 bdae.varData := varData;
130 1 bdae.eqData := eqData;
131 then bdae;
132
133 else algorithm
134 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed with partition type " + Partition.Partition.kindToString(kind) + "!"});
135 ✗ then fail();
136 end match;
137 end main;
138
139 function applyModule
140 input list<Partition> partitions;
141 input BPartition.Kind kind;
142 output list<Partition> new_partitions = {};
143 input output VarData varData;
144 input output EqData eqData;
145 input UnorderedMap<Path, Function> funcMap;
146 input Module.causalizeInterface func;
147 input list<Partition> twins = {} "partitions of nearly the same systems, paired by index";
148 output list<Partition> new_twins = {};
149 protected
150 Partition new_partition;
151 list<Partition> paired, unpaired = twins;
152 Boolean violated = false "true if any partition violated variability consistency";
153 algorithm
154
2/2
✓ Branch 0 taken 505 times.
✓ Branch 1 taken 566 times.
1071 for partition in partitions loop
155 505 (paired, unpaired) := List.splitOnTrue(unpaired, function Partition.hasIndex(index = partition.index));
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 505 times.
505 (new_partition, varData, eqData, paired) := func(partition, varData, eqData, funcMap, paired);
157
2/2
✓ Branch 1 taken 503 times.
✓ Branch 2 taken 1 time.
504 new_partitions := if Partition.isEmpty(new_partition) then new_partitions else new_partition :: new_partitions;
158
5/6
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 504 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 504 times.
510 new_twins := List.append_reverse(list(twin for twin guard(not Partition.isEmpty(twin)) in paired), new_twins);
159 end for;
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 566 times.
566 for twin in unpaired loop
161 ✗ (new_partition, varData, eqData) := func(twin, varData, eqData, funcMap, {});
162 ✗ new_twins := if Partition.isEmpty(new_partition) then new_twins else new_partition :: new_twins;
163 end for;
164 566 new_partitions := listReverse(new_partitions);
165 566 new_twins := listReverse(new_twins);
166
167
2/2
✓ Branch 1 taken 378 times.
✓ Branch 2 taken 188 times.
566 if not Partition.kindIsInitial(kind) then
168
2/2
✓ Branch 0 taken 329 times.
✓ Branch 1 taken 378 times.
707 for partition in new_partitions loop
169
3/4
✓ Branch 1 taken 328 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 328 times.
✗ Branch 4 not taken.
329 violated := checkSystemVariabilities(partition) or violated;
170 end for;
171
2/2
✓ Branch 0 taken 377 times.
✓ Branch 1 taken 1 time.
378 if violated then fail(); end if;
172 end if;
173 end applyModule;
174
175 function checkSystemVariabilities
176 "checks whether variability is valid. Prevents things like `Integer i = time;`"
177 input Partition partition;
178 output Boolean violated = false;
179 protected
180 String err;
181 algorithm
182
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 329 times.
✓ Branch 2 taken 329 times.
✗ Branch 3 not taken.
329 if isSome(partition.strongComponents) then
183
2/2
✓ Branch 2 taken 2432 times.
✓ Branch 3 taken 329 times.
3090 for scc in Util.getOption(partition.strongComponents) loop
184 () := match scc
185 local
186 Type ty1, ty2;
187 TypeCheck.MatchKind kind;
188
189 case StrongComponent.SINGLE_COMPONENT() algorithm
190 1372 ty1 := Type.removeSizeOneArraysAndRecords(Variable.typeOf(Pointer.access(scc.var)));
191 1372 ty2 := Type.removeSizeOneArraysAndRecords(Equation.getType(Pointer.access(scc.eqn)));
192 1372 (_, _, kind) := TypeCheck.matchTypes(ty1, ty2, Expression.fromCref(BVariable.getVarName(scc.var)));
193
194
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1371 times.
1372 if kind <> NFTypeCheck.MatchKind.EXACT then
195 // The variability of the equation must be greater or equal to that of the variable it solves.
196 // See MLS section 3.8 Variability of Expressions
197 1 err := getInstanceName() + " failed. The following strong component has conflicting types: "
198 + Type.toString(ty1) + " != " + Type.toString(ty2) + "\n" + StrongComponent.toString(scc);
199
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if Flags.isSet(Flags.BLT_DUMP) then
200 ✗ err := err + "\n" + Partition.toString(partition);
201 end if;
202 1 Error.addMessage(Error.COMPILER_ERROR, {err});
203 violated := true;
204 end if;
205 then ();
206 /* TODO case StrongComponent.MULTI_COMPONENT() */
207 else ();
208 end match;
209 end for;
210 end if;
211 end checkSystemVariabilities;
212
213 function simple
214 input VariablePointers vars;
215 input EquationPointers eqns;
216 input BPartition.Kind kind;
217 input Adjacency.MatrixStrictness st = NBAdjacency.MatrixStrictness.MATCHING;
218 input Iterator iter = Iterator.EMPTY();
219 output Matching matching;
220 output list<StrongComponent> comps;
221 protected
222 Adjacency.Matrix full, adj;
223 algorithm
224 // create full matrix
225 1112 full := Adjacency.Matrix.createFull(vars, eqns, kind);
226
227 // create solvable adjacency matrix for matching
228 1112 adj := Adjacency.Matrix.fullToFinal(full, vars.map, eqns.map, eqns, st, iter);
229 1112 matching := Matching.regular(NBMatching.EMPTY_MATCHING, adj);
230
231 // create all occurence adjacency matrix for sorting, upgrading the matching matrix
232 1112 adj := Adjacency.Matrix.upgrade(adj, full, vars.map, eqns.map, eqns, NBAdjacency.MatrixStrictness.SORTING);
233 1112 comps := Sorting.tarjan(adj, matching, vars, eqns);
234 end simple;
235
236 function getModule
237 "Returns the module function that was chosen by the user."
238 output Module.causalizeInterface func;
239 protected
240 String flag = Flags.getConfigString(Flags.MATCHING_ALGORITHM);
241 algorithm
242 func := match flag
243 case "PFPlusExt" then causalizePseudoArray;
244 case "pseudo" then causalizePseudoArray;
245 /* ... New causalize modules have to be added here */
246 else algorithm
247 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for unknown option: " + flag});
248 ✗ then fail();
249 end match;
250 end getModule;
251
252 // ############################################################
253 // Protected Functions and Types
254 // ############################################################
255
256 protected
257 function causalizePseudoArray extends Module.causalizeInterface;
258 protected
259 BPartition.Kind kind = Partition.getKind(partition);
260 VariablePointers variables;
261 EquationPointers equations;
262 Adjacency.Matrix full, adj_matching, adj_sorting;
263 Matching matching;
264 list<StrongComponent> comps;
265 list<Partition> new_twins = {};
266 algorithm
267 (variables, equations, full, matching, comps) := match kind
268 local
269 list<Pointer<Variable>> fixable, unfixable;
270 list<Pointer<Equation>> initials, simulation;
271 UnorderedMap<ComponentRef, Integer> vo, vn, eo, en;
272
273 case kind guard(Partition.kindIsInitial(kind)) algorithm
274 // compress the arrays to remove gaps
275 175 partition.unknowns := VariablePointers.compress(partition.unknowns);
276 175 partition.equations := EquationPointers.compress(partition.equations);
277
278 // split the variables and equations
279 175 (fixable, unfixable) := List.splitOnTrue(VariablePointers.toList(partition.unknowns), BVariable.isFixable);
280 175 (initials, simulation) := List.splitOnTrue(EquationPointers.toList(partition.equations), Equation.isInitial);
281
282 // create full matrix
283 175 full := Adjacency.Matrix.createFull(partition.unknowns, partition.equations, kind);
284
285 // do not resolve potential singular partitions in Phase I or II! -> regular matching
286 // #################################################
287 // Phase I: match initial equations <-> unfixable vars
288 // #################################################
289
4/4
✓ Branch 0 taken 2805 times.
✓ Branch 1 taken 175 times.
✓ Branch 2 taken 2805 times.
✓ Branch 3 taken 175 times.
2980 vn := UnorderedMap.subMap(partition.unknowns.map, list(BVariable.getVarName(var) for var in unfixable));
290
4/4
✓ Branch 0 taken 723 times.
✓ Branch 1 taken 175 times.
✓ Branch 2 taken 723 times.
✓ Branch 3 taken 175 times.
898 en := UnorderedMap.subMap(partition.equations.map, list(Equation.getEqnName(eqn) for eqn in initials));
291 175 adj_matching := Adjacency.Matrix.fullToFinal(full, vn, en, partition.equations, NBAdjacency.MatrixStrictness.MATCHING);
292 175 matching := Matching.regular(NBMatching.EMPTY_MATCHING, adj_matching, true, true);
293
294 // #################################################
295 // Phase II: match all equations <-> unfixables
296 // #################################################
297 vo := vn;
298 eo := en;
299 175 vn := UnorderedMap.new<Integer>(ComponentRef.hash, ComponentRef.isEqual);
300
4/4
✓ Branch 0 taken 3335 times.
✓ Branch 1 taken 175 times.
✓ Branch 2 taken 3335 times.
✓ Branch 3 taken 175 times.
3510 en := UnorderedMap.subMap(partition.equations.map, list(Equation.getEqnName(eqn) for eqn in simulation));
301 175 (adj_matching, full) := Adjacency.Matrix.expand(adj_matching, full, vo, vn, eo, en, partition.unknowns, partition.equations, Partition.getKind(partition));
302 175 matching := Matching.regular(matching, adj_matching, true, true);
303
304 // #################################################
305 // Phase III: match all equations <-> all vars
306 // #################################################
307 175 vo := UnorderedMap.merge(vo, vn, sourceInfo());
308 175 eo := UnorderedMap.merge(eo, en, sourceInfo());
309
4/4
✓ Branch 0 taken 380 times.
✓ Branch 1 taken 175 times.
✓ Branch 2 taken 380 times.
✓ Branch 3 taken 175 times.
555 vn := UnorderedMap.subMap(partition.unknowns.map, list(BVariable.getVarName(var) for var in fixable));
310 175 en := UnorderedMap.new<Integer>(ComponentRef.hash, ComponentRef.isEqual);
311 175 (adj_matching, full) := Adjacency.Matrix.expand(adj_matching, full, vo, vn, eo, en, partition.unknowns, partition.equations, Partition.getKind(partition));
312 175 (matching, adj_matching, full, variables, equations, varData, eqData) := Matching.singular(matching, adj_matching, full, partition.unknowns, partition.equations, funcMap, varData, eqData, kind, false, false);
313
314 // create all occurence adjacency matrix for sorting, upgrading the matching matrix
315 175 adj_sorting := Adjacency.Matrix.upgrade(adj_matching, full, variables.map, equations.map, equations, NBAdjacency.MatrixStrictness.SORTING);
316 175 comps := Sorting.tarjan(adj_sorting, matching, variables, equations);
317 175 then (variables, equations, full, matching, comps);
318
319 else algorithm
320 // compress the arrays to remove gaps
321 329 variables := VariablePointers.compress(partition.unknowns);
322 329 equations := EquationPointers.compress(partition.equations);
323
324 // perform ASSC on the system
325 //ASSC.main(equations, variables);
326
327 // create full matrix
328 329 full := Adjacency.Matrix.createFull(variables, equations, kind);
329
330 // create solvable adjacency matrix for matching
331 329 adj_matching := Adjacency.Matrix.fullToFinal(full, variables.map, equations.map, equations, NBAdjacency.MatrixStrictness.MATCHING);
332
333 // perform matching
334 329 (matching, adj_matching, full, variables, equations, varData, eqData) := Matching.singular(NBMatching.EMPTY_MATCHING, adj_matching, full, variables, equations, funcMap, varData, eqData, kind, false);
335
336 // create all occurence adjacency matrix for sorting, upgrading the matching matrix
337 328 adj_sorting := Adjacency.Matrix.upgrade(adj_matching, full, variables.map, equations.map, equations, NBAdjacency.MatrixStrictness.SORTING);
338 328 comps := Sorting.tarjan(adj_sorting, matching, variables, equations);
339 328 then (variables, equations, full, matching, comps);
340 end match;
341
342 1006 partition.unknowns := variables;
343 partition.equations := equations;
344 partition.adjacencyMatrix := SOME(full);
345 partition.matching := SOME(matching);
346 partition.strongComponents := SOME(listArray(comps));
347
348
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 503 times.
509 for twin in twins loop
349 6 (twin, varData, eqData) := causalizeTwin(twin, partition, adj_matching, adj_sorting, funcMap, varData, eqData);
350 new_twins := twin :: new_twins;
351 end for;
352 503 twins := listReverse(new_twins);
353 end causalizePseudoArray;
354
355 function causalizeTwin
356 "causalizes the twin from the seed and falls back to causalizing it from
357 scratch if that fails. The seed's matching was not built by the three
358 matching phases and can leave a variable that is not fixable unmatched
359 in the balancing of the initialization."
360 input output Partition twin;
361 input Partition seed;
362 input Adjacency.Matrix seed_matching;
363 input Adjacency.Matrix seed_sorting;
364 input UnorderedMap<Path, Function> funcMap;
365 input output VarData varData;
366 input output EqData eqData;
367 protected
368 list<Partition> no_twins;
369 algorithm
370 6 ErrorExt.setCheckpoint("NBCausalize.causalizeTwin");
371 try
372 6 (twin, varData, eqData) := causalizeTwinSeeded(twin, seed, seed_matching, seed_sorting, funcMap, varData, eqData);
373 6 ErrorExt.delCheckpoint("NBCausalize.causalizeTwin");
374 else
375 ✗ ErrorExt.rollBack("NBCausalize.causalizeTwin");
376 ✗ (twin, varData, eqData, no_twins) := causalizePseudoArray(twin, varData, eqData, funcMap, {});
377 end try;
378 end causalizeTwin;
379
380 function causalizeTwinSeeded
381 "causalizes a partition over the same variables as the causalized seed and
382 nearly the same equations: the rows of equal equations are taken from the
383 seed's matrices and its matching is the starting point"
384 input output Partition twin;
385 input Partition seed;
386 input Adjacency.Matrix seed_matching;
387 input Adjacency.Matrix seed_sorting;
388 input UnorderedMap<Path, Function> funcMap;
389 input output VarData varData;
390 input output EqData eqData;
391 protected
392 BPartition.Kind kind = Partition.getKind(twin);
393 VariablePointers variables;
394 EquationPointers equations;
395 Adjacency.Matrix full, balanced, adj_matching, adj_sorting;
396 Matching matching;
397 array<Integer> seed_index;
398 list<StrongComponent> comps;
399 algorithm
400 6 twin.unknowns := VariablePointers.compress(twin.unknowns);
401 twin.equations := EquationPointers.compress(twin.equations);
402 6 full := Adjacency.Matrix.createFull(twin.unknowns, twin.equations, kind);
403 6 seed_index := Adjacency.Matrix.equalRows(seed.equations, seed.unknowns, twin.equations, twin.unknowns);
404 6 adj_matching := Adjacency.Matrix.upgradeFrom(NBAdjacency.Matrix.EMPTY(NBAdjacency.MatrixStrictness.FULL), full, twin.unknowns.map, twin.equations.map, twin.equations, NBAdjacency.MatrixStrictness.MATCHING, seed_matching, seed_index);
405 6 matching := Matching.fromSeed(seed, adj_matching, twin.unknowns, twin.equations);
406 6 (matching, adj_matching, balanced, variables, equations, varData, eqData) := Matching.singular(matching, adj_matching, full, twin.unknowns, twin.equations, funcMap, varData, eqData, kind, false, false);
407
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if referenceEq(balanced, full) then
408 6 adj_sorting := Adjacency.Matrix.upgradeFrom(adj_matching, full, variables.map, equations.map, equations, NBAdjacency.MatrixStrictness.SORTING, seed_sorting, seed_index);
409 else
410 full := balanced;
411 ✗ adj_sorting := Adjacency.Matrix.upgrade(adj_matching, full, variables.map, equations.map, equations, NBAdjacency.MatrixStrictness.SORTING);
412 end if;
413 6 comps := Sorting.tarjan(adj_sorting, matching, variables, equations);
414
415 18 twin.unknowns := variables;
416 twin.equations := equations;
417 twin.adjacencyMatrix := SOME(full);
418 twin.matching := SOME(matching);
419 twin.strongComponents := SOME(listArray(comps));
420 end causalizeTwinSeeded;
421
422 function causalizeDAEMode extends Module.causalizeInterface;
423 algorithm
424 // nothing to do?
425 end causalizeDAEMode;
426
427 annotation(__OpenModelica_Interface="nbackend");
428 end NBCausalize;
429