Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 59.0% 85 / 0 / 144
Functions: -% 0 / 1 / 1
Branches: 52.6% 61 / 0 / 116

OMCompiler/Compiler/NBackEnd/Classes/NBPartition.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 NBPartition
37 "file: NBPartition.mo
38 package: NBPartition
39 description: This file contains the data-types used to process individual
40 partitions of equations.
41 "
42
43 public
44 import Adjacency = NBAdjacency;
45 import Matching = NBMatching;
46 import StrongComponent = NBStrongComponent;
47
48 protected
49 // NF imports
50 import Call = NFCall;
51 import ClockKind = NFClockKind;
52 import ComponentRef = NFComponentRef;
53 import Expression = NFExpression;
54 import Type = NFType;
55 import Variable = NFVariable;
56
57 // Backend Imports
58 import BackendDAE = NBackendDAE;
59 import BEquation = NBEquation;
60 import NBEquation.{Equation, EquationKind};
61 import BJacobian = NBJacobian;
62 import NBEquation.EquationPointers;
63 import NBPartitioning.{BClock, ClockedInfo};
64 import Jacobian = NBackendDAE.BackendDAE;
65 import BVariable = NBVariable;
66 import NBVariable.VariablePointers;
67
68 // Util imports
69 import DoubleEnded;
70 import StringUtil;
71
72 public
73 type Kind = enumeration(ODE, ALG, ODE_EVT, ALG_EVT, INI, INI_0, DAE, JAC, CLK);
74
75 uniontype Association
76 record CONTINUOUS
77 Kind kind;
78 Option<Jacobian> jacobian "Analytic jacobian for the integrator";
79 Option<Jacobian> jacobianAdjoint "Analytic adjoint jacobian for the integrator";
80 Option<Jacobian> LFG_jacobian "Analytic jacobian of Lagrange term (L), ODE (f), Path Constraints (g) for MOO";
81 Option<Jacobian> MRF_jacobian "Analytic jacobian of Mayer term (Mf), Final Constraints (rf) for MOO";
82 Option<Jacobian> R0_jacobian "Analytic jacobian of Initial Constraints (r0) for MOO";
83 end CONTINUOUS;
84
85 record CLOCKED
86 BClock clock;
87 Option<BClock> baseClock;
88 UnorderedSet<BClock> clock_deps "dependencies of this clocked partition";
89 Boolean holdEvents;
90 end CLOCKED;
91
92 function toStringShort
93 input Association association;
94 output String str;
95 algorithm
96 str := match association
97 18 case CONTINUOUS() then "Continuous " + Partition.kindToString(association.kind);
98 case CLOCKED() then "Clocked";
99 else "Unknown";
100 end match;
101 end toStringShort;
102
103 function toString
104 input Association association;
105 output String str;
106 algorithm
107 str := match association
108 case CONTINUOUS() algorithm
109
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if isSome(association.jacobian) then
110 ✗ str := BJacobian.toString(Util.getOption(association.jacobian), Partition.kindToString(association.kind));
111 ✗ if (Flags.getConfigBool(Flags.MOO_DYNAMIC_OPTIMIZATION)) then
112 ✗ str := "\n" + str + BJacobian.toString(Util.getOption(association.LFG_jacobian), Partition.kindToString(association.kind));
113 ✗ str := "\n" + str + BJacobian.toString(Util.getOption(association.MRF_jacobian), Partition.kindToString(association.kind));
114 ✗ str := "\n" + str + BJacobian.toString(Util.getOption(association.R0_jacobian), Partition.kindToString(association.kind));
115 end if;
116 else
117 3 str := StringUtil.headline_1("No Jacobian");
118 end if;
119
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 2 times.
3 if isSome(association.jacobianAdjoint) then
120 1 str := BJacobian.toString(Util.getOption(association.jacobianAdjoint), Partition.kindToString(association.kind) + " Adjoint") + "\n";
121 end if;
122 then str;
123 case CLOCKED() algorithm
124 ✗ str := BClock.toString(association.clock);
125 ✗ if isSome(association.baseClock) then
126 ✗ str := StringUtil.headline_1("Sub clock: " + str + " of base clock " + BClock.toString(Util.getOption(association.baseClock)));
127 else
128 ✗ str := StringUtil.headline_1("Base clock: " + str);
129 end if;
130 then str;
131 else algorithm
132 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Unknown partition association in match."});
133 ✗ then fail();
134 end match;
135 end toString;
136
137 function create
138 "create an association for a partition from the equation array and the clocked info
139 holdEvents is updated later for clocked associations"
140 input EquationPointers equations;
141 input Kind kind;
142 input ClockedInfo info;
143 input UnorderedSet<ComponentRef> infer_del;
144 output Association association;
145 protected
146 Pointer<Option<ClockTpl>> clock_ptr = Pointer.create(NONE());
147 Pointer<Option<ComponentRef>> infer_ptr = Pointer.create(NONE());
148 UnorderedSet<ClockTpl> failed_set = UnorderedSet.new(hashClockTpl, isEqualClockTpl);
149 UnorderedSet<BClock> clock_deps = UnorderedSet.new(BClock.hash, BClock.isEqual);
150 Option<ClockTpl> clock_tpl;
151 Option<ComponentRef> infer;
152 ComponentRef name, base_name;
153 BClock clock;
154 algorithm
155 330 EquationPointers.mapExp(equations, function expClocked(
156 info = info, clock_ptr = clock_ptr, infer_ptr = infer_ptr, failed_set = failed_set, clock_deps = clock_deps, infer_del = infer_del), NONE(), Expression.fakeMap);
157
158 // get the clock tuple and the inferred reference
159 330 clock_tpl := Pointer.access(clock_ptr);
160 330 infer := Pointer.access(infer_ptr);
161
162
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
✓ Branch 2 taken 311 times.
✓ Branch 3 taken 19 times.
330 if isSome(clock_tpl) then
163 19 SOME((name, clock)) := clock_tpl;
164
165 // throw an error if there are different clocks in this partition
166
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if not UnorderedSet.isEmpty(failed_set) then
167 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because there are non-identical clocks in the same partition:\n"
168 + "### First clock found:\n" + clockTplString((name, clock)) + "\n### Conflicting clocks:\n" + UnorderedSet.toString(failed_set, clockTplString) + "."});
169 ✗ fail();
170 end if;
171
172
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 15 times.
19 if BClock.isBaseClock(clock) then
173 // if the clock is still an inferred clock without reference, update it to the default base clock and add to the base clocks
174
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if BClock.isInferredClock(clock) then
175 ✗ if isNone(infer) then
176 clock := NBPartitioning.DEFAULT_BASE_CLOCK;
177 ✗ UnorderedMap.add(name, clock, info.baseClocks);
178 else
179 ✗ clock := BClock.INFERRED_CLOCK(Util.getOption(infer));
180 end if;
181 end if;
182 4 association := CLOCKED(clock, NONE(), clock_deps, false);
183 else
184 15 base_name := UnorderedMap.getSafe(name, info.subToBase, sourceInfo());
185 30 association := CLOCKED(clock, SOME(UnorderedMap.getSafe(base_name, info.baseClocks, sourceInfo())), clock_deps, false);
186 end if;
187 else
188 311 association := CONTINUOUS(kind, NONE(), NONE(), NONE(), NONE(), NONE());
189 end if;
190 end create;
191
192 function merge
193 input output Association ass1;
194 input Association ass2;
195 input Boolean strict;
196 algorithm
197 ass1 := match (ass1, ass2)
198 local
199 BackendDAE jac1, jac2;
200
201 // merging jacobians
202 case (CONTINUOUS(jacobian = SOME(jac1 as BackendDAE.JACOBIAN())), CONTINUOUS(jacobian = SOME(jac2))) guard(ass1.kind == ass2.kind or not strict) algorithm
203 ✗ ass1.jacobian := SOME(BJacobian.combine({jac1, jac2}, jac1.name));
204 then ass1;
205
206 // no jacobians to merge
207 case (CONTINUOUS(), CONTINUOUS()) guard(ass1.kind == ass2.kind or not strict) then ass1;
208
209 // merging clocked partitions
210 case (CLOCKED(), CLOCKED()) guard(not strict or (BClock.isEqual(ass1.clock, ass2.clock) and Util.optionEqual(ass1.baseClock, ass2.baseClock, BClock.isEqual))) algorithm
211 3 ass1.clock_deps := UnorderedSet.union(ass1.clock_deps, ass2.clock_deps);
212
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
6 ass1.holdEvents := ass1.holdEvents or ass2.holdEvents;
213 then ass1;
214
215 // unmergable
216 else algorithm
217 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Cannot merge\n" + toString(ass1) + " and\n" + toString(ass2) + "."});
218 ✗ then fail();
219 end match;
220 end merge;
221
222 function isClocked
223 input Association association;
224 output Boolean b;
225 algorithm
226 b := match association case CLOCKED() then true; else false; end match;
227 end isClocked;
228
229 // clock tpl for collecting and comparing clocks in a partition
230 type ClockTpl = tuple<ComponentRef, BClock>;
231
232 function clockTplString
233 input ClockTpl tpl;
234 output String str = "(" + ComponentRef.toString(Util.tuple21(tpl)) + " = " + BClock.toString(Util.tuple22(tpl)) + ")";
235 end clockTplString;
236
237 function hashClockTpl
238 input ClockTpl tpl;
239 output Integer hash;
240 algorithm
241 ✗ hash := ComponentRef.hash(Util.tuple21(tpl));
242 ✗ hash := stringHashDjb2Continue(BClock.toString(Util.tuple22(tpl)), hash);
243 end hashClockTpl;
244
245 function isEqualClockTpl
246 input ClockTpl tpl1;
247 input ClockTpl tpl2;
248 output Boolean b = ComponentRef.isEqual(Util.tuple21(tpl1), Util.tuple21(tpl2)) and
249 BClock.isEqual(Util.tuple22(tpl1), Util.tuple22(tpl2));
250 end isEqualClockTpl;
251
252 protected
253 function expClocked
254 "checks if an expression is a clock and collects it. Also finds all other clock dependencies"
255 input output Expression exp "the examined expression";
256 input ClockedInfo info "contains all base- and sub-clocks";
257 input Pointer<Option<ClockTpl>> clock_ptr "the first found clock";
258 input Pointer<Option<ComponentRef>> infer_ptr "potential inferred clock dependency";
259 input UnorderedSet<ClockTpl> failed_set "clocks that are not equal to the first found clock";
260 input UnorderedSet<BClock> clock_deps "clock dependencies found in sub sampling functions";
261 input UnorderedSet<ComponentRef> infer_del "inferred clocks that can be removed";
262 algorithm
263 exp := match exp
264 local
265 Option<BClock> clock_opt;
266 ComponentRef arg;
267
268 // check if its a variable that defines a clock
269 case Expression.CREF() guard(BVariable.isClockOrClocked(BVariable.getVarPointer(exp.cref, sourceInfo()))) algorithm
270
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 16 times.
22 if UnorderedMap.contains(exp.cref, info.baseClocks) then
271 6 clock_opt := SOME(UnorderedMap.getSafe(exp.cref, info.baseClocks, sourceInfo()));
272 elseif UnorderedMap.contains(exp.cref, info.subClocks) then
273 16 clock_opt := SOME( UnorderedMap.getSafe(exp.cref, info.subClocks, sourceInfo()));
274 else
275 clock_opt := NONE();
276 end if;
277
278 () := match (clock_opt, Pointer.access(clock_ptr))
279 local
280 BClock new, old;
281 ComponentRef name;
282
283 // old sub clock and new base clock -> potentially update inferred base clock
284 case (SOME(BClock.BASE_CLOCK()), SOME((name, BClock.SUB_CLOCK()))) algorithm
285 ✗ removeInferredClock(name, exp.cref, info, infer_del);
286 then ();
287
288 // old base clock getting updated to new sub clock
289 case (SOME(new as BClock.SUB_CLOCK()), SOME((_, BClock.BASE_CLOCK()))) algorithm
290 ✗ Pointer.update(clock_ptr, SOME((exp.cref, new)));
291 then ();
292
293 // old base clock is inferred --> always take new clock
294 case (SOME(new), SOME((_, BClock.BASE_CLOCK(clock = ClockKind.INFERRED_CLOCK())))) algorithm
295 4 Pointer.update(clock_ptr, SOME((exp.cref, new)));
296 then ();
297
298 // clocks -> equal: success / different: fail
299 case (SOME(new), SOME((_, old))) algorithm
300 // if the old clock is inferred just override it
301
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if BClock.isInferredClock(old) then
302 ✗ Pointer.update(clock_ptr, SOME((exp.cref, new)));
303 elseif not (BClock.isInferredClock(new) or BClock.isEqual(new, old)) then
304 ✗ UnorderedSet.add((exp.cref, new), failed_set);
305 end if;
306 then ();
307
308 // new clock
309 case (SOME(new), NONE()) algorithm
310 38 Pointer.update(clock_ptr, SOME((exp.cref, new)));
311 then ();
312
313 else ();
314 end match;
315 then exp;
316
317 // only look for clock dependencies on sample functions
318 case Expression.CALL(call = Call.TYPED_CALL(arguments = Expression.CREF(cref = arg) :: _)) guard(Expression.isClockOrSampleFunction(exp)) algorithm
319
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 7 times.
13 if UnorderedMap.contains(arg, info.subClocks) then
320 6 UnorderedSet.add(UnorderedMap.getSafe(arg, info.subClocks, sourceInfo()), clock_deps);
321 6 Pointer.update(infer_ptr, SOME(arg));
322 end if;
323 then exp;
324
325 // go deeper on everything else
326 21345 else Expression.mapShallow(exp, function expClocked(info = info, clock_ptr = clock_ptr, infer_ptr = infer_ptr, failed_set = failed_set, clock_deps = clock_deps, infer_del = infer_del));
327 end match;
328 end expClocked;
329
330 protected
331 function removeInferredClock
332 input ComponentRef name;
333 input ComponentRef new_name;
334 input ClockedInfo info;
335 input UnorderedSet<ComponentRef> infer_del;
336 protected
337 BClock base;
338 ComponentRef base_name;
339 list<ComponentRef> sub_clock_names1, sub_clock_names2;
340 algorithm
341 ✗ base_name := UnorderedMap.getSafe(name, info.subToBase, sourceInfo());
342 ✗ base := UnorderedMap.getSafe(base_name, info.baseClocks, sourceInfo());
343 ✗ if BClock.isInferredClock(base) then
344 // update the sub clock (and all other related ones) to the correct base clock
345 ✗ sub_clock_names1 := UnorderedMap.getSafe(base_name, info.baseToSub, sourceInfo());
346 ✗ for s_name in sub_clock_names1 loop
347 ✗ UnorderedMap.add(s_name, new_name, info.subToBase);
348 end for;
349 // add all the sub clocks to the correct base clock
350 ✗ sub_clock_names2 := UnorderedMap.getOrDefault(new_name, info.baseToSub, {});
351 ✗ UnorderedMap.add(new_name, listAppend(sub_clock_names1, sub_clock_names2), info.baseToSub);
352 // add the old inferred clock to clocks that can be removed
353 ✗ UnorderedSet.add(base_name, infer_del);
354 end if;
355 end removeInferredClock;
356 end Association;
357
358 uniontype Partition
359 record PARTITION
360 Integer index "Partition index";
361 Association association "Clocked/Continuous";
362 VariablePointers unknowns "Variable array of unknowns, subset of full variable array";
363 Option<VariablePointers> daeUnknowns "Variable array of unknowns in the case of dae mode";
364 EquationPointers equations "Equations array, subset of the full equation array";
365 Option<Adjacency.Matrix> adjacencyMatrix "Adjacency matrix with all additional information";
366 Option<Matching> matching "Matching (see 2.5)";
367 Option<array<StrongComponent>> strongComponents "Strong Components";
368 end PARTITION;
369
370 function toString
371 input Partition partition;
372 input Integer level = 0;
373 output String str;
374 algorithm
375 18 str := StringUtil.headline_2("(" + intString(partition.index) + ") " + Association.toStringShort(partition.association) + " Partition") + "\n";
376 str := match partition.strongComponents
377 local
378 array<StrongComponent> comps;
379
380 case SOME(comps) algorithm
381
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
69 for i in 1:arrayLength(comps) loop
382 53 str := str + StrongComponent.toString(comps[i], i) + "\n";
383 end for;
384 then str;
385
386 else algorithm
387 2 str := str + VariablePointers.toString(partition.unknowns, "Unknown") + "\n" + EquationPointers.toString(partition.equations, "") + "\n";
388 then str;
389 end match;
390
391
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if level == 1 or level == 3 then
392 ✗ if isSome(partition.adjacencyMatrix) then
393 ✗ str := str + Adjacency.Matrix.toString(Util.getOption(partition.adjacencyMatrix)) + "\n";
394 end if;
395
396 ✗ if isSome(partition.matching) then
397 ✗ str := str + Matching.toString(Util.getOption(partition.matching)) + "\n";
398 end if;
399 end if;
400
401
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
18 if level == 2 then
402 3 str := str + Association.toString(partition.association) + "\n";
403 end if;
404 end toString;
405
406 function toStringList
407 input list<Partition> partitions;
408 input String header = "";
409 output String str = "";
410 algorithm
411
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 15 times.
54 if not listEmpty(partitions) then
412
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
15 if header <> "" then
413 15 str := StringUtil.headline_1(header) + "\n";
414 end if;
415
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
30 for part in partitions loop
416 15 str := str + toString(part);
417 end for;
418 end if;
419 end toStringList;
420
421 function sort
422 input output Partition partition;
423 algorithm
424 ✗ partition.unknowns := VariablePointers.sort(partition.unknowns);
425 partition.equations := EquationPointers.sort(partition.equations);
426 end sort;
427
428 function hasIndex
429 input Partition partition;
430 input Integer index;
431 output Boolean b = partition.index == index;
432 end hasIndex;
433
434 function isEmpty
435 "returns true if the partition is empty.
436 maybe check more than only equations?"
437 input Partition partition;
438 output Boolean b = EquationPointers.size(partition.equations) == 0
439 or Util.applyOptionOrDefault(partition.strongComponents, isEmptyArr, false);
440 protected
441 function isEmptyArr = arrayEmpty; // FIXME MetaModelica bug with inlined functions?
442 end isEmpty;
443
444 function isODEorDAE
445 input Partition part;
446 output Boolean b;
447 algorithm
448 b := match part.association
449 local
450 Kind kind;
451
4/4
✓ Branch 0 taken 407 times.
✓ Branch 1 taken 82 times.
✓ Branch 2 taken 406 times.
✓ Branch 3 taken 1 time.
489 case Association.CONTINUOUS(kind = kind) then kind == Kind.ODE or kind == Kind.ODE_EVT or kind == Kind.DAE;
452 else false;
453 end match;
454 end isODEorDAE;
455
456 function isClocked
457 input Partition part;
458 output Boolean b;
459 algorithm
460 b := match part.association
461 local
462 case Association.CLOCKED() then true;
463 else false;
464 end match;
465 end isClocked;
466
467 function categorize
468 input Partition partition;
469 input DoubleEnded.MutableList<Partition> ode;
470 input DoubleEnded.MutableList<Partition> alg;
471 input DoubleEnded.MutableList<Partition> ode_evt;
472 input DoubleEnded.MutableList<Partition> alg_evt;
473 input DoubleEnded.MutableList<Partition> clocked;
474 protected
475 Boolean algebraic, continuous;
476 Kind kind;
477 Association association;
478 function isAlgebraicContinuous
479 input Partition part;
480 output Boolean alg = true;
481 output Boolean con = true;
482 algorithm
483
2/2
✓ Branch 1 taken 2519 times.
✓ Branch 2 taken 310 times.
2829 for var in VariablePointers.toList(part.unknowns) loop
484
4/4
✓ Branch 0 taken 2313 times.
✓ Branch 1 taken 206 times.
✓ Branch 3 taken 83 times.
✓ Branch 4 taken 2230 times.
2519 alg := if alg then not BVariable.isStateDerivative(var) else false;
485
4/4
✓ Branch 0 taken 1631 times.
✓ Branch 1 taken 888 times.
✓ Branch 3 taken 75 times.
✓ Branch 4 taken 1556 times.
2519 con := if con then not BVariable.isDiscrete(var) else false;
486 // stop searching if both
487
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 2502 times.
2519 if not (alg or con) then
488 break;
489 end if;
490 end for;
491 end isAlgebraicContinuous;
492 algorithm
493 327 (algebraic, continuous) := isAlgebraicContinuous(partition);
494 kind := match (algebraic, continuous)
495 case (true, true) then Kind.ALG;
496 case (false, true) then Kind.ODE;
497 case (true, false) then Kind.ALG_EVT;
498 case (false, false) then Kind.ODE_EVT;
499 else fail();
500 end match;
501 partition.association := match (kind, partition.association)
502 case (_, Association.CLOCKED()) algorithm
503 18 DoubleEnded.push_back(clocked, partition);
504 then partition.association;
505 case (Kind.ALG, association as Association.CONTINUOUS()) algorithm
506 168 association.kind := kind;
507 168 partition.association := association;
508 168 DoubleEnded.push_back(alg, partition);
509 then association;
510 case (Kind.ODE, association as Association.CONTINUOUS()) algorithm
511 66 association.kind := kind;
512 66 partition.association := association;
513 66 DoubleEnded.push_back(ode, partition);
514 then association;
515 case (Kind.ALG_EVT, association as Association.CONTINUOUS()) algorithm
516 58 association.kind := kind;
517 58 partition.association := association;
518 58 DoubleEnded.push_back(alg_evt, partition);
519 then association;
520 case (Kind.ODE_EVT, association as Association.CONTINUOUS()) algorithm
521 17 association.kind := kind;
522 17 partition.association := association;
523 17 DoubleEnded.push_back(ode_evt, partition);
524 then association;
525 else fail();
526 end match;
527 end categorize;
528
529 function setIndex
530 input output Partition part;
531 input Pointer<Integer> index;
532 protected
533 Integer clock_idx = Pointer.access(index);
534 algorithm
535 329 part.index := clock_idx;
536
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 311 times.
329 if isClocked(part) then
537 36 part.equations := EquationPointers.map(part.equations, function Equation.setKind(kind = EquationKind.CLOCKED, clock_idx = SOME(clock_idx)));
538 end if;
539 329 Pointer.update(index, clock_idx + 1);
540 end setIndex;
541
542 function setKind
543 input output Partition part;
544 input Kind kind;
545 algorithm
546 part.association := match part.association
547 local
548 Association ass;
549 case ass as Association.CONTINUOUS() algorithm
550 6 ass.kind := kind;
551 then ass;
552 else algorithm
553 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Cannot set kind for non-continuous partition:\n" + toString(part)});
554 ✗ then fail();
555 end match;
556 end setKind;
557
558 function getJacobian
559 input Partition part;
560 output Option<Jacobian> jac;
561 algorithm
562 jac := match part.association
563 case CONTINUOUS(jacobian = jac) then jac;
564 else NONE();
565 end match;
566 end getJacobian;
567
568 function getJacobianAdjoint
569 input Partition part;
570 output Option<Jacobian> jac;
571 algorithm
572 jac := match part.association
573 case CONTINUOUS(jacobianAdjoint = jac) then jac;
574 else NONE();
575 end match;
576 end getJacobianAdjoint;
577
578 function getJacobianLfg
579 input Partition part;
580 output Option<Jacobian> jac;
581 algorithm
582 jac := match part.association
583 case CONTINUOUS(LFG_jacobian = jac) then jac;
584 else NONE();
585 end match;
586 end getJacobianLfg;
587
588 function getJacobianMrf
589 input Partition part;
590 output Option<Jacobian> jac;
591 algorithm
592 jac := match part.association
593 case CONTINUOUS(MRF_jacobian = jac) then jac;
594 else NONE();
595 end match;
596 end getJacobianMrf;
597
598 function getJacobianR0
599 input Partition part;
600 output Option<Jacobian> jac;
601 algorithm
602 jac := match part.association
603 case CONTINUOUS(R0_jacobian = jac) then jac;
604 else NONE();
605 end match;
606 end getJacobianR0;
607
608 function getKind
609 input Partition part;
610 output Kind kind;
611 algorithm
612 kind := match part.association
613 case Association.CONTINUOUS(kind = kind) then kind;
614 else Kind.CLK;
615 end match;
616 end getKind;
617
618 function getClocks
619 input Partition part;
620 output BClock clock;
621 output Option<BClock> baseClock;
622 output Boolean holdEvents;
623 algorithm
624 (clock, baseClock, holdEvents) := match part.association
625 case Association.CLOCKED(clock = clock, baseClock = baseClock, holdEvents = holdEvents) then (clock, baseClock, holdEvents);
626 else algorithm
627 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Cannot get clocks for continuous partition:\n" + toString(part)});
628 ✗ then fail();
629 end match;
630 end getClocks;
631
632 function setClocks
633 input output Partition part;
634 input BClock clock;
635 input Option<BClock> baseClock;
636 algorithm
637 part := match part.association
638 local
639 Association association;
640 case association as Association.CLOCKED() algorithm
641 19 association.clock := clock;
642 19 association.baseClock := baseClock;
643 19 part.association := association;
644 then part;
645 else algorithm
646 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Cannot set clocks for continuous partition:\n" + toString(part)});
647 ✗ then fail();
648 end match;
649 end setClocks;
650
651 function getClockDependencies
652 input Partition part;
653 output UnorderedSet<BClock> clock_deps;
654 algorithm
655 clock_deps := match part.association
656 case Association.CLOCKED(clock_deps = clock_deps) then clock_deps;
657 else algorithm
658 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Cannot get clock dependencies for continuous partition:\n" + toString(part)});
659 ✗ then fail();
660 end match;
661 end getClockDependencies;
662
663 function getLoopResiduals
664 input Partition part;
665 output list<Pointer<Variable>> residuals = {};
666 algorithm
667
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 483 times.
✓ Branch 2 taken 483 times.
✗ Branch 3 not taken.
483 if isSome(part.strongComponents) then
668
2/2
✓ Branch 2 taken 5524 times.
✓ Branch 3 taken 483 times.
6490 for comp in Util.getOption(part.strongComponents) loop
669 5524 residuals := listAppend(StrongComponent.getLoopResiduals(comp), residuals);
670 end for;
671 end if;
672 end getLoopResiduals;
673
674 function mapEqn
675 input output Partition partition;
676 input MapFunc func;
677 partial function MapFunc
678 input output BEquation.Equation e;
679 end MapFunc;
680 algorithm
681 508 partition.equations := EquationPointers.map(partition.equations, func);
682 end mapEqn;
683
684 function mapExp
685 input output Partition partition;
686 input MapFunc func;
687 partial function MapFunc
688 input output Expression e;
689 end MapFunc;
690 algorithm
691 508 partition.equations := EquationPointers.mapExp(partition.equations, func);
692 end mapExp;
693
694 function mapStrongComponents
695 input output Partition partition;
696 input MapFunc func;
697 partial function MapFunc
698 input output StrongComponent comp;
699 end MapFunc;
700 protected
701 array<StrongComponent> comps;
702 algorithm
703
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 if isSome(partition.strongComponents) then
704
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 SOME(comps) := partition.strongComponents;
705
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
446 for i in 1:arrayLength(comps) loop
706
1/2
✓ Branch 0 taken 439 times.
✗ Branch 1 not taken.
439 comps[i] := func(comps[i]);
707 end for;
708 7 partition.strongComponents := SOME(comps);
709 end if;
710 end mapStrongComponents;
711
712 function kindToString
713 input Kind kind;
714 output String str = "";
715 algorithm
716 str := match kind
717 case Kind.ODE then "ODE";
718 case Kind.ALG then "ALG";
719 case Kind.ODE_EVT then "ODE_EVT";
720 case Kind.ALG_EVT then "ALG_EVT";
721 case Kind.INI then "INI";
722 case Kind.INI_0 then "INI_0";
723 case Kind.DAE then "DAE";
724 case Kind.JAC then "JAC";
725 case Kind.CLK then "CLK";
726 else algorithm
727 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Unknown partition kind in match."});
728 ✗ then fail();
729 end match;
730 end kindToString;
731
732 function kindToInteger
733 input Kind kind;
734 output Integer i;
735 algorithm
736 i := match kind
737 case Kind.ODE then 0;
738 case Kind.ALG then 1;
739 case Kind.ODE_EVT then 2;
740 case Kind.ALG_EVT then 3;
741 case Kind.INI then 4;
742 case Kind.INI_0 then 5;
743 case Kind.DAE then 6;
744 case Kind.JAC then 7;
745 case Kind.CLK then 8;
746 else algorithm
747 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Unknown partition kind in match."});
748 ✗ then fail();
749 end match;
750 end kindToInteger;
751
752 function clone
753 "only clones equations."
754 input output Partition par;
755 input Boolean shallow = true;
756 algorithm
757 6 par.equations := EquationPointers.clone(par.equations, shallow);
758 // these are partially pointer based and have to be recomputed if not shallow
759
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if not shallow then
760 6 par.adjacencyMatrix := NONE();
761 par.matching := NONE();
762 par.strongComponents := NONE();
763 par.association := match par.association
764 local
765 Association association;
766 case association as Association.CONTINUOUS() algorithm
767 6 association.jacobian := NONE();
768 then association;
769 else par.association;
770 end match;
771 end if;
772 end clone;
773
774 function removeAlias
775 "removes alias strong components and replaces it with their original strong components.
776 used before differentiating for jacobians."
777 input output Partition par;
778 protected
779 array<StrongComponent> comps;
780 algorithm
781 ✗ if isSome(par.strongComponents) then
782 // no need to override comps afterwards since arrays are mutable
783 ✗ comps := Util.getOption(par.strongComponents);
784 ✗ for i in 1:arrayLength(comps) loop
785 ✗ comps[i] := StrongComponent.removeAlias(comps[i]);
786 end for;
787 end if;
788 end removeAlias;
789
790 function updateHeldVars
791 input output Partition par;
792 input UnorderedSet<ComponentRef> held_crefs;
793 algorithm
794 par.association := match par.association
795 local
796 Association association;
797 case association as Association.CLOCKED() algorithm
798
1/2
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
38 association.holdEvents := not UnorderedSet.isDisjoint(held_crefs, UnorderedMap.keySet(par.unknowns.map));
799 then association;
800 else par.association;
801 end match;
802 end updateHeldVars;
803
804 function merge
805 input output Partition part1;
806 input Partition part2;
807 input Boolean strict;
808 algorithm
809
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
3 if isSome(part1.daeUnknowns) or isSome(part2.daeUnknowns) then
810 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Cannot merge DAE-Mode partitions."}); fail();
811 elseif isSome(part1.strongComponents) or isSome(part2.strongComponents) then
812 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Should not merge sorted partitions."}); fail();
813 elseif isSome(part1.matching) or isSome(part2.matching) then
814 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Should not merge matched partitions."}); fail();
815 elseif isSome(part1.adjacencyMatrix) or isSome(part2.adjacencyMatrix) then
816 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. Should not merge partitions with adjacency matrix."}); fail();
817 end if;
818
819 // index irrelevant, should be updated after use
820 3 part1.association := Association.merge(part1.association, part2.association, strict);
821 part1.unknowns := VariablePointers.addList(VariablePointers.toList(part2.unknowns), part1.unknowns);
822 part1.equations := EquationPointers.addList(EquationPointers.toList(part2.equations), part1.equations);
823 end merge;
824 end Partition;
825
826
827 function kindIsInitial
828 input Kind kind;
829 output Boolean b = kind == Kind.INI or kind == Kind.INI_0;
830 end kindIsInitial;
831
832 annotation(__OpenModelica_Interface="nbackend");
833 end NBPartition;
834