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