/
NBJacobian.mo
712 lines (633 loc) · 29.7 KB
/
NBJacobian.mo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
/*
* This file is part of OpenModelica.
*
* Copyright (c) 1998-2020, Open Source Modelica Consortium (OSMC),
* c/o Linköpings universitet, Department of Computer and Information Science,
* SE-58183 Linköping, Sweden.
*
* All rights reserved.
*
* THIS PROGRAM IS PROVIDED UNDER THE TERMS OF GPL VERSION 3 LICENSE OR
* THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.2.
* ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
* RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GPL VERSION 3,
* ACCORDING TO RECIPIENTS CHOICE.
*
* The OpenModelica software and the Open Source Modelica
* Consortium (OSMC) Public License (OSMC-PL) are obtained
* from OSMC, either from the above address,
* from the URLs: http://www.ida.liu.se/projects/OpenModelica or
* http://www.openmodelica.org, and in the OpenModelica distribution.
* GNU version 3 is obtained from: http://www.gnu.org/copyleft/gpl.html.
*
* This program is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
* IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
*
* See the full OSMC Public License conditions for more details.
*
*/
encapsulated package NBJacobian
"file: NBJacobian.mo
package: NBJacobian
description: This file contains the functions to create and manipulate jacobians.
The main type is inherited from NBackendDAE.mo
NOTE: There is no real jacobian type, it is a BackendDAE.
"
public
import BackendDAE = NBackendDAE;
import Module = NBModule;
protected
// NF imports
import ComponentRef = NFComponentRef;
import Expression = NFExpression;
import NFFlatten.FunctionTree;
import Operator = NFOperator;
import SimplifyExp = NFSimplifyExp;
import Type = NFType;
import Variable = NFVariable;
// Backend imports
import Adjacency = NBAdjacency;
import BEquation = NBEquation;
import BVariable = NBVariable;
import Differentiate = NBDifferentiate;
import NBDifferentiate.{DifferentiationArguments, DifferentiationType};
import NBEquation.{Equation, EquationPointers, EqData};
import Jacobian = NBackendDAE.BackendDAE;
import Matching = NBMatching;
import Replacements = NBReplacements;
import Sorting = NBSorting;
import StrongComponent = NBStrongComponent;
import System = NBSystem;
import NFOperator.{MathClassification, SizeClassification};
import NBVariable.{VariablePointers, VarData};
// Util imports
import AvlSetPath;
import StringUtil;
import UnorderedMap;
import UnorderedSet;
import Util;
public
type JacobianType = enumeration(SIMULATION, NONLINEAR);
function main
"Wrapper function for any jacobian function. This will be called during
simulation and gets the corresponding subfunction from Config."
extends Module.wrapper;
input System.SystemType systemType;
protected
constant Module.jacobianInterface func = getModule();
algorithm
bdae := match bdae
local
String name "Context name for jacobian";
VariablePointers knowns "Variable array of knowns";
FunctionTree funcTree "Function call bodies";
list<System.System> oldSystems, newSystems = {} "Equation systems before and afterwards";
list<System.System> oldEvents, newEvents = {} "Event Equation systems before and afterwards";
case BackendDAE.MAIN(varData = BVariable.VAR_DATA_SIM(knowns = knowns), funcTree = funcTree)
algorithm
(oldSystems, oldEvents, name) := match systemType
case NBSystem.SystemType.ODE then (bdae.ode, bdae.ode_event, "ODE_JAC");
case NBSystem.SystemType.DAE then (Util.getOption(bdae.dae), bdae.ode_event,"DAE_JAC");
else algorithm
Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for: " + System.System.systemTypeString(systemType)});
then fail();
end match;
if Flags.isSet(Flags.JAC_DUMP) then
print(StringUtil.headline_1("[symjacdump] Creating symbolic Jacobians:") + "\n");
end if;
for syst in listReverse(oldSystems) loop
(syst, funcTree) := systJacobian(syst, funcTree, knowns, name, func);
newEvents := syst::newEvents;
end for;
for syst in listReverse(oldEvents) loop
(syst, funcTree) := systJacobian(syst, funcTree, knowns, name, func);
newEvents := syst::newEvents;
end for;
_ := match systemType
case NBSystem.SystemType.ODE algorithm
bdae.ode := newSystems;
bdae.ode_event := newEvents;
then ();
case NBSystem.SystemType.DAE algorithm
bdae.dae := SOME(newSystems);
bdae.ode_event := newEvents;
then ();
end match;
bdae.funcTree := funcTree;
then bdae;
else algorithm
// maybe add failtrace here and allow failing
Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for: " + BackendDAE.toString(bdae)});
then fail();
end match;
end main;
function nonlinear
input VariablePointers variables;
input EquationPointers equations;
input array<StrongComponent> comps;
output Option<Jacobian> jacobian;
input output FunctionTree funcTree;
input String name;
algorithm
(jacobian, funcTree) := jacobianSymbolic(
name = name,
jacType = JacobianType.NONLINEAR,
seedCandidates = variables,
partialCandidates = EquationPointers.getResiduals(equations), // these have to be updated once there are inner equations in torn systems
equations = equations,
knowns = VariablePointers.empty(0), // remove them? are they necessary?
strongComponents = SOME(comps),
funcTree = funcTree
);
end nonlinear;
function combine
input list<BackendDAE> jacobians;
input String name;
output BackendDAE jacobian;
protected
JacobianType jacType;
list<Pointer<Variable>> variables = {}, unknowns = {}, knowns = {}, auxiliaryVars = {}, aliasVars = {};
list<Pointer<Variable>> diffVars = {}, dependencies = {}, resultVars = {}, tmpVars = {}, seedVars = {};
list<StrongComponent> comps = {};
list<SparsityPatternCol> col_wise_pattern = {};
list<SparsityPatternRow> row_wise_pattern = {};
list<ComponentRef> seed_vars = {};
list<ComponentRef> partial_vars = {};
Integer nnz = 0;
VarData varData;
EqData eqData;
SparsityPattern sparsityPattern;
SparsityColoring sparsityColoring = SparsityColoring.lazy(EMPTY_SPARSITY_PATTERN, UnorderedMap.new<CrefLst>(ComponentRef.hash, ComponentRef.isEqual));
algorithm
if listLength(jacobians) == 1 then
jacobian := List.first(jacobians);
jacobian := match jacobian case BackendDAE.JACOBIAN() algorithm jacobian.name := name; then jacobian; end match;
else
for jac in jacobians loop
_ := match jac
local
VarData tmpVarData;
SparsityPattern tmpPattern;
case BackendDAE.JACOBIAN(varData = tmpVarData as VarData.VAR_DATA_JAC(), sparsityPattern = tmpPattern) algorithm
jacType := jac.jacType;
variables := listAppend(VariablePointers.toList(tmpVarData.variables), variables);
unknowns := listAppend(VariablePointers.toList(tmpVarData.unknowns), unknowns);
knowns := listAppend(VariablePointers.toList(tmpVarData.knowns), knowns);
auxiliaryVars := listAppend(VariablePointers.toList(tmpVarData.auxiliaries), auxiliaryVars);
aliasVars := listAppend(VariablePointers.toList(tmpVarData.aliasVars), aliasVars);
diffVars := listAppend(VariablePointers.toList(tmpVarData.diffVars), diffVars);
dependencies := listAppend(VariablePointers.toList(tmpVarData.dependencies), dependencies);
resultVars := listAppend(VariablePointers.toList(tmpVarData.resultVars), resultVars);
tmpVars := listAppend(VariablePointers.toList(tmpVarData.tmpVars), tmpVars);
seedVars := listAppend(VariablePointers.toList(tmpVarData.seedVars), seedVars);
comps := listAppend(arrayList(jac.comps), comps);
col_wise_pattern := listAppend(tmpPattern.col_wise_pattern, col_wise_pattern);
row_wise_pattern := listAppend(tmpPattern.row_wise_pattern, row_wise_pattern);
seed_vars := listAppend(tmpPattern.seed_vars, seed_vars);
partial_vars := listAppend(tmpPattern.partial_vars, partial_vars);
nnz := nnz + tmpPattern.nnz;
sparsityColoring := SparsityColoring.combine(sparsityColoring, jac.sparsityColoring);
then ();
else algorithm
Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for\n" + BackendDAE.toString(jac)});
then fail();
end match;
end for;
varData := VarData.VAR_DATA_JAC(
variables = VariablePointers.fromList(variables),
unknowns = VariablePointers.fromList(unknowns),
knowns = VariablePointers.fromList(knowns),
auxiliaries = VariablePointers.fromList(auxiliaryVars),
aliasVars = VariablePointers.fromList(aliasVars),
diffVars = VariablePointers.fromList(diffVars),
dependencies = VariablePointers.fromList(dependencies),
resultVars = VariablePointers.fromList(resultVars),
tmpVars = VariablePointers.fromList(tmpVars),
seedVars = VariablePointers.fromList(seedVars)
);
sparsityPattern := SPARSITY_PATTERN(
col_wise_pattern = col_wise_pattern,
row_wise_pattern = row_wise_pattern,
seed_vars = seed_vars,
partial_vars = partial_vars,
nnz = nnz
);
jacobian := BackendDAE.JACOBIAN(
name = name,
jacType = jacType,
varData = varData,
comps = listArray(comps),
sparsityPattern = sparsityPattern,
sparsityColoring = sparsityColoring
);
end if;
end combine;
function getModule
"Returns the module function that was chosen by the user."
output Module.jacobianInterface func;
algorithm
if Flags.getConfigBool(Flags.GENERATE_SYMBOLIC_JACOBIAN) then
func := jacobianSymbolic;
else
func := jacobianNumeric;
end if;
end getModule;
function toString
input BackendDAE jacobian;
input output String str;
algorithm
str := BackendDAE.toString(jacobian, str);
end toString;
function jacobianTypeString
input JacobianType jacType;
output String str;
algorithm
str := match jacType
case JacobianType.SIMULATION then "[SIM]";
case JacobianType.NONLINEAR then "[NLS]";
else "[ERR]";
end match;
end jacobianTypeString;
// necessary as wrapping value type for UnorderedMap
type CrefLst = list<ComponentRef>;
type SparsityPatternCol = tuple<ComponentRef, CrefLst> "partial_vars, {seed_vars}";
type SparsityPatternRow = SparsityPatternCol "seed_vars, {partial_vars}";
uniontype SparsityPattern
record SPARSITY_PATTERN
list<SparsityPatternCol> col_wise_pattern "colum-wise sparsity pattern";
list<SparsityPatternRow> row_wise_pattern "row-wise sparsity pattern";
list<ComponentRef> seed_vars "independent variables solved here ($SEED)";
list<ComponentRef> partial_vars "LHS variables of the jacobian ($pDER)";
Integer nnz "number of nonzero elements";
end SPARSITY_PATTERN;
function toString
input SparsityPattern pattern;
output String str = StringUtil.headline_2("Sparsity Pattern (nnz: " + intString(pattern.nnz) + ")");
protected
ComponentRef cref;
list<ComponentRef> dependencies;
Boolean colEmpty = listEmpty(pattern.col_wise_pattern);
Boolean rowEmpty = listEmpty(pattern.row_wise_pattern);
algorithm
if not colEmpty then
str := str + "\n" + StringUtil.headline_3("### Columns ###");
for col in pattern.col_wise_pattern loop
(cref, dependencies) := col;
str := str + "(" + ComponentRef.toString(cref) + ")\t affects:\t" + ComponentRef.listToString(dependencies) + "\n";
end for;
end if;
if not rowEmpty then
str := str + "\n" + StringUtil.headline_3("##### Rows #####");
for row in pattern.row_wise_pattern loop
(cref, dependencies) := row;
str := str + "(" + ComponentRef.toString(cref) + ")\t depends on:\t" + ComponentRef.listToString(dependencies) + "\n";
end for;
end if;
end toString;
function create
input VariablePointers seedCandidates;
input VariablePointers partialCandidates;
input Option<array<StrongComponent>> strongComponents "Strong Components";
input JacobianType jacType;
output SparsityPattern sparsityPattern;
output SparsityColoring sparsityColoring;
protected
UnorderedMap<ComponentRef, CrefLst> map;
algorithm
(sparsityPattern, map) := match strongComponents
local
array<StrongComponent> comps;
list<ComponentRef> seed_vars, seed_vars_array, partial_vars, tmp;
UnorderedSet<ComponentRef> set;
list<SparsityPatternCol> cols = {};
list<SparsityPatternRow> rows = {};
Integer nnz = 0;
case SOME(comps) guard(arrayEmpty(comps)) algorithm
then (EMPTY_SPARSITY_PATTERN, UnorderedMap.new<CrefLst>(ComponentRef.hash, ComponentRef.isEqual));
case SOME(comps) algorithm
// get all relevant crefs
partial_vars := VariablePointers.getVarNames(VariablePointers.scalarize(partialCandidates));
seed_vars := VariablePointers.getVarNames(VariablePointers.scalarize(seedCandidates));
// unscalarized seed vars are currently needed for sparsity pattern
seed_vars_array := VariablePointers.getVarNames(seedCandidates);
// create a sufficiant big unordered map
map := UnorderedMap.new<CrefLst>(ComponentRef.hash, ComponentRef.isEqual, Util.nextPrime(listLength(seed_vars) + listLength(partial_vars)));
set := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual, Util.nextPrime(listLength(seed_vars_array)));
// save all seed_vars to know later on if a cref should be added
for cref in seed_vars loop
UnorderedMap.add(cref, {}, map);
end for;
for cref in seed_vars_array loop
UnorderedSet.add(cref, set);
end for;
// traverse all components and save cref dependencies (only column-wise)
for i in 1:arrayLength(comps) loop
StrongComponent.collectCrefs(comps[i], map, set, not partialCandidates.scalarized, jacType);
end for;
// create row-wise sparsity pattern
for cref in partial_vars loop
// only create rows for derivatives
if jacType == JacobianType.NONLINEAR or BVariable.checkCref(cref, BVariable.isStateDerivative) then
if UnorderedMap.contains(cref, map) then
tmp := List.uniqueOnTrue(UnorderedMap.getSafe(cref, map), ComponentRef.isEqual);
rows := (cref, tmp) :: rows;
for dep in tmp loop
// also add inverse dependency (indep var) --> (res/tmp) :: rest
UnorderedMap.add(dep, cref :: UnorderedMap.getSafe(dep, map), map);
end for;
end if;
end if;
end for;
// create column-wise sparsity pattern
for cref in seed_vars loop
tmp := List.uniqueOnTrue(UnorderedMap.getSafe(cref, map), ComponentRef.isEqual);
cols := (cref, tmp) :: cols;
end for;
// find number of nonzero elements
for col in cols loop
(_, tmp) := col;
nnz := nnz + listLength(tmp);
end for;
then (SPARSITY_PATTERN(cols, rows, seed_vars, partial_vars, nnz), map);
case NONE() algorithm
Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because of missing strong components."});
then fail();
else algorithm
Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed!"});
then fail();
end match;
// create coloring
sparsityColoring := SparsityColoring.PartialD2ColoringAlg(sparsityPattern, map);
if Flags.isSet(Flags.DUMP_SPARSE) then
print(toString(sparsityPattern) + "\n" + SparsityColoring.toString(sparsityColoring));
end if;
end create;
function createEmpty
output SparsityPattern sparsityPattern = EMPTY_SPARSITY_PATTERN;
output SparsityColoring sparsityColoring = EMPTY_SPARSITY_COLORING;
end createEmpty;
end SparsityPattern;
constant SparsityPattern EMPTY_SPARSITY_PATTERN = SPARSITY_PATTERN({}, {}, {}, {}, 0);
constant SparsityColoring EMPTY_SPARSITY_COLORING = SPARSITY_COLORING(listArray({}), listArray({}));
type SparsityColoringCol = list<ComponentRef> "seed variable lists belonging to the same color";
type SparsityColoringRow = SparsityColoringCol "partial variable lists for each color (multiples allowed!)";
uniontype SparsityColoring
record SPARSITY_COLORING
"column wise coloring with extra row sparsity information"
array<SparsityColoringCol> cols;
array<SparsityColoringRow> rows;
end SPARSITY_COLORING;
function toString
input SparsityColoring sparsityColoring;
output String str = StringUtil.headline_2("Sparsity Coloring");
protected
Boolean empty = arrayLength(sparsityColoring.cols) == 0;
algorithm
if empty then
str := str + "\n<empty sparsity pattern>\n";
end if;
for i in 1:arrayLength(sparsityColoring.cols) loop
str := str + "Color (" + intString(i) + ")\n"
+ " - Column: " + ComponentRef.listToString(sparsityColoring.cols[i]) + "\n"
+ " - Row: " + ComponentRef.listToString(sparsityColoring.rows[i]) + "\n\n";
end for;
end toString;
function lazy
"creates a lazy coloring that just groups each independent variable individually
and implies dependence for each row"
input SparsityPattern sparsityPattern;
input UnorderedMap<ComponentRef, CrefLst> map;
output SparsityColoring sparsityColoring;
protected
array<SparsityColoringCol> cols;
array<SparsityColoringRow> rows;
algorithm
cols := listArray(list({cref} for cref in sparsityPattern.seed_vars));
rows := arrayCreate(arrayLength(cols), sparsityPattern.partial_vars);
sparsityColoring := SPARSITY_COLORING(cols, rows);
end lazy;
function PartialD2ColoringAlg
"author: kabdelhak 2022-03
taken from: 'What Color Is Your Jacobian? Graph Coloring for Computing Derivatives'
https://doi.org/10.1137/S0036144504444711
A greedy partial distance-2 coloring algorithm. Slightly adapted to also track row sparsity."
input SparsityPattern sparsityPattern;
input UnorderedMap<ComponentRef, CrefLst> map;
output SparsityColoring sparsityColoring;
protected
array<ComponentRef> cref_lookup;
UnorderedMap<ComponentRef, Integer> index_lookup;
array<Boolean> color_exists;
array<Integer> coloring, forbidden_colors;
array<list<ComponentRef>> col_coloring, row_coloring;
Integer color;
list<SparsityColoringCol> cols_lst = {};
list<SparsityColoringRow> rows_lst = {};
algorithm
// integer to cref and reverse lookup arrays
cref_lookup := listArray(sparsityPattern.seed_vars);
index_lookup := UnorderedMap.new<Integer>(ComponentRef.hash, ComponentRef.isEqual, Util.nextPrime(listLength(sparsityPattern.seed_vars)));
for i in 1:arrayLength(cref_lookup) loop
UnorderedMap.add(cref_lookup[i], i, index_lookup);
end for;
// create empty colorings
coloring := arrayCreate(arrayLength(cref_lookup), 0);
forbidden_colors := arrayCreate(arrayLength(cref_lookup), 0);
color_exists := arrayCreate(arrayLength(cref_lookup), false);
col_coloring := arrayCreate(arrayLength(cref_lookup), {});
row_coloring := arrayCreate(arrayLength(cref_lookup), {});
for i in 1:arrayLength(cref_lookup) loop
for row_var /* w */ in UnorderedMap.getSafe(cref_lookup[i], map) loop
for col_var /* x */ in UnorderedMap.getSafe(row_var, map) loop
color := coloring[UnorderedMap.getSafe(col_var, index_lookup)];
if color > 0 then
forbidden_colors[color] := i;
end if;
end for;
end for;
(_, color) := Array.findFirstOnTrueWithIdx(forbidden_colors, function intNe(i2 = i));
coloring[i] := color;
// also save all row dependencies of this color
row_coloring[color] := listAppend(row_coloring[color], UnorderedMap.getSafe(cref_lookup[i], map));
color_exists[color] := true;
end for;
for i in 1:arrayLength(coloring) loop
col_coloring[coloring[i]] := cref_lookup[i] :: col_coloring[coloring[i]];
end for;
// traverse in reverse to have correct ordering in the end)
for i in arrayLength(color_exists):-1:1 loop
if color_exists[i] then
cols_lst := col_coloring[i] :: cols_lst;
rows_lst := row_coloring[i] :: rows_lst;
end if;
end for;
sparsityColoring := SPARSITY_COLORING(listArray(cols_lst), listArray(rows_lst));
end PartialD2ColoringAlg;
function combine
"combines sparsity patterns by just appending them because they are supposed to
be entirely independent of each other."
input SparsityColoring coloring1;
input SparsityColoring coloring2;
output SparsityColoring coloring_out;
protected
SparsityColoring smaller_coloring;
algorithm
// append the smaller to the bigger
(coloring_out, smaller_coloring) := if arrayLength(coloring2.cols) > arrayLength(coloring1.cols) then (coloring2, coloring1) else (coloring1, coloring2);
for i in 1:arrayLength(smaller_coloring.cols) loop
coloring_out.cols[i] := listAppend(coloring_out.cols[i], smaller_coloring.cols[i]);
coloring_out.rows[i] := listAppend(coloring_out.rows[i], smaller_coloring.rows[i]);
end for;
end combine;
end SparsityColoring;
protected
// ToDo: all the DAEMode stuff is probably incorrect!
function systJacobian
input output System.System syst;
input output FunctionTree funcTree;
input VariablePointers knowns;
input String name "Context name for jacobian";
input Module.jacobianInterface func;
protected
list<Pointer<Variable>> derivative_vars, state_vars;
VariablePointers seedCandidates, partialCandidates;
Option<Jacobian> jacobian "Resulting jacobian";
algorithm
partialCandidates := syst.unknowns;
derivative_vars := list(var for var guard(BVariable.isStateDerivative(var)) in VariablePointers.toList(syst.unknowns));
state_vars := list(BVariable.getStateVar(var) for var in derivative_vars);
seedCandidates := VariablePointers.fromList(state_vars, partialCandidates.scalarized);
(jacobian, funcTree) := func(name, JacobianType.SIMULATION, seedCandidates, partialCandidates, syst.equations, knowns, syst.strongComponents, funcTree);
syst.jacobian := jacobian;
if Flags.isSet(Flags.JAC_DUMP) then
print(System.System.toString(syst, 2));
end if;
end systJacobian;
function jacobianSymbolic extends Module.jacobianInterface;
protected
list<StrongComponent> comps, diffed_comps;
Pointer<list<Pointer<Variable>>> seed_vars_ptr = Pointer.create({});
Pointer<list<Pointer<Variable>>> pDer_vars_ptr = Pointer.create({});
Pointer<UnorderedMap<ComponentRef,ComponentRef>> jacobianHT = Pointer.create(UnorderedMap.new<ComponentRef>(ComponentRef.hash, ComponentRef.isEqual));
Option<UnorderedMap<ComponentRef,ComponentRef>> optHT;
Differentiate.DifferentiationArguments diffArguments;
Pointer<Integer> idx = Pointer.create(0);
list<Pointer<Variable>> all_vars, unknown_vars, aux_vars, alias_vars, depend_vars, res_vars, tmp_vars, seed_vars;
BVariable.VarData varDataJac;
SparsityPattern sparsityPattern;
SparsityColoring sparsityColoring;
algorithm
if Util.isSome(strongComponents) then
// filter all discrete strong components and differentiate the others
// todo: mixed algebraic loops should be here without the discrete subsets
comps := list(comp for comp guard(not StrongComponent.isDiscrete(comp)) in Util.getOption(strongComponents));
else
Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because no strong components were given!"});
end if;
// create seed and pDer vars (also filters out discrete vars)
VariablePointers.mapPtr(seedCandidates, function makeVarTraverse(name = name, vars_ptr = seed_vars_ptr, ht = jacobianHT, makeVar = BVariable.makeSeedVar));
VariablePointers.mapPtr(partialCandidates, function makeVarTraverse(name = name, vars_ptr = pDer_vars_ptr, ht = jacobianHT, makeVar = BVariable.makePDerVar));
optHT := SOME(Pointer.access(jacobianHT));
// Build differentiation argument structure
diffArguments := Differentiate.DIFFERENTIATION_ARGUMENTS(
diffCref = ComponentRef.EMPTY(), // no explicit cref necessary, rules are set by HT
new_vars = {},
jacobianHT = optHT, // seed and temporary cref hashtable
diffType = NBDifferentiate.DifferentiationType.JACOBIAN,
funcTree = funcTree,
scalarized = seedCandidates.scalarized
);
// differentiate all strong components
(diffed_comps, diffArguments) := Differentiate.differentiateStrongComponentList(comps, diffArguments, idx, name, getInstanceName());
funcTree := diffArguments.funcTree;
// collect var data (most of this can be removed)
unknown_vars := listReverse(Pointer.access(pDer_vars_ptr));
all_vars := unknown_vars; // add other vars later on
seed_vars := Pointer.access(seed_vars_ptr);
aux_vars := seed_vars; // add other auxiliaries later on
alias_vars := {};
depend_vars := {};
res_vars := {};
tmp_vars := {}; // ToDo: add this once system has been torn
varDataJac := BVariable.VAR_DATA_JAC(
variables = VariablePointers.fromList(all_vars),
unknowns = VariablePointers.fromList(unknown_vars),
knowns = knowns,
auxiliaries = VariablePointers.fromList(aux_vars),
aliasVars = VariablePointers.fromList(alias_vars),
diffVars = partialCandidates,
dependencies = VariablePointers.fromList(depend_vars),
resultVars = VariablePointers.fromList(res_vars),
tmpVars = VariablePointers.fromList(tmp_vars),
seedVars = VariablePointers.fromList(seed_vars)
);
(sparsityPattern, sparsityColoring) := SparsityPattern.create(seedCandidates, partialCandidates, strongComponents, jacType);
jacobian := SOME(Jacobian.JACOBIAN(
name = name,
jacType = jacType,
varData = varDataJac,
comps = listArray(diffed_comps),
sparsityPattern = sparsityPattern,
sparsityColoring = sparsityColoring
));
end jacobianSymbolic;
function jacobianNumeric "still creates sparsity pattern"
extends Module.jacobianInterface;
protected
VarData varDataJac;
SparsityPattern sparsityPattern;
SparsityColoring sparsityColoring;
protected
Pointer<Integer> idx = Pointer.create(0);
algorithm
varDataJac := BVariable.VAR_DATA_JAC(
variables = VariablePointers.fromList({}),
unknowns = partialCandidates,
knowns = VariablePointers.fromList({}),
auxiliaries = VariablePointers.fromList({}),
aliasVars = VariablePointers.fromList({}),
diffVars = VariablePointers.fromList({}),
dependencies = VariablePointers.fromList({}),
resultVars = VariablePointers.fromList({}),
tmpVars = VariablePointers.fromList({}),
seedVars = seedCandidates
);
(sparsityPattern, sparsityColoring) := SparsityPattern.create(seedCandidates, partialCandidates, strongComponents, jacType);
jacobian := SOME(Jacobian.JACOBIAN(
name = name,
jacType = jacType,
varData = varDataJac,
comps = listArray({}),
sparsityPattern = sparsityPattern,
sparsityColoring = sparsityColoring
));
end jacobianNumeric;
function makeVarTraverse
input Pointer<Variable> var_ptr;
input String name;
input Pointer<list<Pointer<Variable>>> vars_ptr;
input Pointer<UnorderedMap<ComponentRef,ComponentRef>> ht;
input Func makeVar;
partial function Func
input output ComponentRef cref;
input String name;
output Pointer<Variable> new_var_ptr;
end Func;
protected
Variable var = Pointer.access(var_ptr);
ComponentRef cref;
Pointer<Variable> new_var_ptr;
algorithm
// only create seed or pDer var if it is continuous
if BVariable.isContinuous(var_ptr) then
(cref, new_var_ptr) := makeVar(var.name, name);
// add $<new>.x variable pointer to the variables
Pointer.update(vars_ptr, new_var_ptr :: Pointer.access(vars_ptr));
// add x -> $<new>.x to the hashTable for later lookup
UnorderedMap.add(var.name, cref, Pointer.access(ht)); // PHI: Pointer.update ?
end if;
end makeVarTraverse;
annotation(__OpenModelica_Interface="backend");
end NBJacobian;