OMCompiler/Compiler/NBackEnd/Modules/1_Main/NBPartitioning.mo
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of OpenModelica. | ||
| 3 | * | ||
| 4 | * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), | ||
| 5 | * c/o Linköpings universitet, Department of Computer and Information Science, | ||
| 6 | * SE-58183 Linköping, Sweden. | ||
| 7 | * | ||
| 8 | * All rights reserved. | ||
| 9 | * | ||
| 10 | * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR | ||
| 11 | * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. | ||
| 12 | * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES | ||
| 13 | * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL | ||
| 14 | * VERSION 3, ACCORDING TO RECIPIENTS CHOICE. | ||
| 15 | * | ||
| 16 | * The OpenModelica software and the OSMC (Open Source Modelica Consortium) | ||
| 17 | * Public License (OSMC-PL) are obtained from OSMC, either from the above | ||
| 18 | * address, from the URLs: | ||
| 19 | * http://www.openmodelica.org or | ||
| 20 | * https://github.com/OpenModelica/ or | ||
| 21 | * http://www.ida.liu.se/projects/OpenModelica, | ||
| 22 | * and in the OpenModelica distribution. | ||
| 23 | * | ||
| 24 | * GNU AGPL version 3 is obtained from: | ||
| 25 | * https://www.gnu.org/licenses/licenses.html#GPL | ||
| 26 | * | ||
| 27 | * This program is distributed WITHOUT ANY WARRANTY; without | ||
| 28 | * even the implied warranty of MERCHANTABILITY or FITNESS | ||
| 29 | * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH | ||
| 30 | * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. | ||
| 31 | * | ||
| 32 | * See the full OSMC Public License conditions for more details. | ||
| 33 | * | ||
| 34 | */ | ||
| 35 | |||
| 36 | encapsulated package NBPartitioning | ||
| 37 | "file: NBPartitioning.mo | ||
| 38 | package: NBPartitioning | ||
| 39 | description: This file contains the functions for the partitioning module. | ||
| 40 | " | ||
| 41 | |||
| 42 | public | ||
| 43 | import Module = NBModule; | ||
| 44 | |||
| 45 | protected | ||
| 46 | // NF | ||
| 47 | import NFBackendExtension.{BackendInfo, VariableKind}; | ||
| 48 | import Call = NFCall; | ||
| 49 | import ClockKind = NFClockKind; | ||
| 50 | import ComponentRef = NFComponentRef; | ||
| 51 | import Expression = NFExpression; | ||
| 52 | import NFFunction.Function; | ||
| 53 | import Type = NFType; | ||
| 54 | import Variable = NFVariable; | ||
| 55 | |||
| 56 | // Backend | ||
| 57 | import Adjacency = NBAdjacency; | ||
| 58 | import BackendDAE = NBackendDAE; | ||
| 59 | import Causalize = NBCausalize; | ||
| 60 | import BEquation = NBEquation; | ||
| 61 | import NBEquation.{Equation, EquationPointer, EquationPointers, EqData, EquationKind, WhenEquationBody, WhenStatement}; | ||
| 62 | import Matching = NBMatching; | ||
| 63 | import Sorting = NBSorting; | ||
| 64 | import StrongComponent = NBStrongComponent; | ||
| 65 | import Partition = NBPartition; | ||
| 66 | import BVariable = NBVariable; | ||
| 67 | import NBVariable.{VariablePointer, VariablePointers, VarData}; | ||
| 68 | |||
| 69 | // Util | ||
| 70 | import MetaModelica.Dangerous; | ||
| 71 | import DoubleEnded; | ||
| 72 | import Rational; | ||
| 73 | import NBBackendUtil; | ||
| 74 | import PointerWeak; | ||
| 75 | import UnorderedMap; | ||
| 76 | import UnorderedSet; | ||
| 77 | |||
| 78 | // Old imports | ||
| 79 | import OldDAE = DAE; | ||
| 80 | import OldBackendDAE = BackendDAE; | ||
| 81 | |||
| 82 | public | ||
| 83 | uniontype BClock | ||
| 84 | record BASE_CLOCK | ||
| 85 | ClockKind clock; | ||
| 86 | end BASE_CLOCK; | ||
| 87 | |||
| 88 | record SUB_CLOCK | ||
| 89 | Rational factor; | ||
| 90 | Rational shift; | ||
| 91 | Option<String> solver; | ||
| 92 | end SUB_CLOCK; | ||
| 93 | |||
| 94 | record INFERRED_CLOCK | ||
| 95 | ComponentRef base_ref; | ||
| 96 | end INFERRED_CLOCK; | ||
| 97 | |||
| 98 | function toString | ||
| 99 | input BClock clock; | ||
| 100 | output String str; | ||
| 101 | algorithm | ||
| 102 | str := match clock | ||
| 103 | 85 | case BASE_CLOCK() then ClockKind.toDebugString(clock.clock); | |
| 104 | 72 | case SUB_CLOCK() then "SUB_CLOCK(" + Rational.toString(clock.factor) + ", " + Rational.toString(clock.shift) + ")"; | |
| 105 | ✗ | case INFERRED_CLOCK() then "INFERRED_CLOCK(" + ComponentRef.toString(clock.base_ref) + ")"; | |
| 106 | else "UNKNOWN_CLOCK()"; | ||
| 107 | end match; | ||
| 108 | end toString; | ||
| 109 | |||
| 110 | function hash | ||
| 111 | input BClock clock; | ||
| 112 | output Integer i = stringHashDjb2(toString(clock)); | ||
| 113 | end hash; | ||
| 114 | |||
| 115 | function isEqual | ||
| 116 | input BClock clock1; | ||
| 117 | input BClock clock2; | ||
| 118 | output Boolean b; | ||
| 119 | algorithm | ||
| 120 | b := match (clock1, clock2) | ||
| 121 | 57 | case (BASE_CLOCK(), BASE_CLOCK()) then ClockKind.compare(clock1.clock, clock2.clock) == 0; | |
| 122 |
4/6✓ Branch 1 taken 14 times.
✓ Branch 2 taken 3 times.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
|
17 | case (SUB_CLOCK(), SUB_CLOCK()) then Rational.isEqual(clock1.factor, clock2.factor) and Rational.isEqual(clock1.shift, clock2.shift) and Util.optionEqual(clock1.solver, clock2.solver, stringEq); |
| 123 | ✗ | case (INFERRED_CLOCK(), INFERRED_CLOCK()) then ComponentRef.isEqual(clock1.base_ref, clock2.base_ref); | |
| 124 | else false; | ||
| 125 | end match; | ||
| 126 | end isEqual; | ||
| 127 | |||
| 128 | function add | ||
| 129 | input Equation eqn; | ||
| 130 | input ClockedInfo info; | ||
| 131 | algorithm | ||
| 132 | () := match (Equation.getLHS(eqn), Equation.getRHS(eqn)) | ||
| 133 | local | ||
| 134 | ComponentRef clock_name; | ||
| 135 | Expression exp; | ||
| 136 | |||
| 137 | case (SOME(Expression.CREF(cref = clock_name)), SOME(exp)) | ||
| 138 | guard(Expression.isClockOrSampleFunction(exp)) algorithm | ||
| 139 | 27 | create(clock_name, exp, info); | |
| 140 | then (); | ||
| 141 | |||
| 142 | case (SOME(exp), SOME(Expression.CREF(cref = clock_name))) | ||
| 143 | guard(Expression.isClockOrSampleFunction(exp)) algorithm | ||
| 144 | ✗ | create(clock_name, exp, info); | |
| 145 | then (); | ||
| 146 | |||
| 147 | else (); | ||
| 148 | end match; | ||
| 149 | end add; | ||
| 150 | |||
| 151 | function isBaseClock | ||
| 152 | input BClock clock; | ||
| 153 | output Boolean b; | ||
| 154 | algorithm | ||
| 155 | b := match clock case BASE_CLOCK() then true; else false; end match; | ||
| 156 | end isBaseClock; | ||
| 157 | |||
| 158 | function isInferredClock | ||
| 159 | input BClock clock; | ||
| 160 | output Boolean b; | ||
| 161 | algorithm | ||
| 162 | b := match clock | ||
| 163 | case BASE_CLOCK(clock = ClockKind.INFERRED_CLOCK()) then true; | ||
| 164 | case INFERRED_CLOCK() then true; | ||
| 165 | else false; | ||
| 166 | end match; | ||
| 167 | end isInferredClock; | ||
| 168 | |||
| 169 | function isEventClock | ||
| 170 | input BClock clock; | ||
| 171 | output Boolean b; | ||
| 172 | algorithm | ||
| 173 | b := match clock case BASE_CLOCK(clock = ClockKind.EVENT_CLOCK()) then true; else false; end match; | ||
| 174 | end isEventClock; | ||
| 175 | |||
| 176 | function baseClockInferrence | ||
| 177 | input output BClock clock; | ||
| 178 | input UnorderedMap<ComponentRef, BClock> base_clock_inferrence; | ||
| 179 | algorithm | ||
| 180 | clock := match clock | ||
| 181 | local | ||
| 182 | BClock base_clock; | ||
| 183 | case INFERRED_CLOCK() algorithm | ||
| 184 | ✗ | base_clock := UnorderedMap.getSafe(clock.base_ref, base_clock_inferrence, sourceInfo()); | |
| 185 | ✗ | then baseClockInferrence(base_clock, base_clock_inferrence); | |
| 186 | case BClock.BASE_CLOCK(clock = ClockKind.INFERRED_CLOCK()) then DEFAULT_BASE_CLOCK; | ||
| 187 | else clock; | ||
| 188 | end match; | ||
| 189 | end baseClockInferrence; | ||
| 190 | |||
| 191 | function convertBase | ||
| 192 | input BClock clock; | ||
| 193 | output OldDAE.ClockKind oldClock; | ||
| 194 | algorithm | ||
| 195 | oldClock := match clock | ||
| 196 | 11 | case BASE_CLOCK() then ClockKind.toDAE(clock.clock); | |
| 197 | else algorithm | ||
| 198 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for non-base clock: " + toString(clock)}); | |
| 199 | ✗ | then fail(); | |
| 200 | end match; | ||
| 201 | end convertBase; | ||
| 202 | |||
| 203 | function convertSub | ||
| 204 | input BClock clock; | ||
| 205 | output OldBackendDAE.SubClock oldClock; | ||
| 206 | algorithm | ||
| 207 | oldClock := match clock | ||
| 208 | 18 | case SUB_CLOCK() then OldBackendDAE.SUBCLOCK( | |
| 209 | factor = NBBackendUtil.convertRational(clock.factor), | ||
| 210 | shift = NBBackendUtil.convertRational(clock.shift), | ||
| 211 | solver = clock.solver); | ||
| 212 | else algorithm | ||
| 213 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for non-sub clock: " + toString(clock)}); | |
| 214 | ✗ | then fail(); | |
| 215 | end match; | ||
| 216 | end convertSub; | ||
| 217 | |||
| 218 | function toExp | ||
| 219 | input BClock clock; | ||
| 220 | output Expression exp; | ||
| 221 | algorithm | ||
| 222 | exp := match clock | ||
| 223 | 2 | case BASE_CLOCK() then Expression.CLKCONST(clock.clock); | |
| 224 | else algorithm | ||
| 225 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for non-base clock: " + toString(clock)}); | |
| 226 | ✗ | then fail(); | |
| 227 | end match; | ||
| 228 | end toExp; | ||
| 229 | |||
| 230 | protected | ||
| 231 | function create | ||
| 232 | input ComponentRef clock_name; | ||
| 233 | input Expression exp; | ||
| 234 | input ClockedInfo info; | ||
| 235 | protected | ||
| 236 | BClock clock; | ||
| 237 | Option<ComponentRef> baseClock; | ||
| 238 | Pointer<Variable> clock_var; | ||
| 239 | algorithm | ||
| 240 | try | ||
| 241 | // parse the clock and see if it depends on another clock | ||
| 242 | 27 | (clock, baseClock) := fromExp(exp); | |
| 243 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 15 times.
|
27 | if isSome(baseClock) then |
| 244 | // sub clock | ||
| 245 | 15 | UnorderedMap.add(clock_name, clock, info.subClocks); | |
| 246 | 15 | UnorderedMap.add(clock_name, Util.getOption(baseClock), info.subToBase); | |
| 247 | else | ||
| 248 | // base clock | ||
| 249 | 12 | UnorderedMap.add(clock_name, clock, info.baseClocks); | |
| 250 | end if; | ||
| 251 | |||
| 252 | // if this is from the equation block and not from variable binding, the variable needs to updated | ||
| 253 | // such that the clock can be found for the partitioning clocked association | ||
| 254 | 27 | clock_var := BVariable.getVarPointer(clock_name, sourceInfo()); | |
| 255 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 24 times.
|
27 | if not BVariable.isClockOrClocked(clock_var) then |
| 256 | 3 | BVariable.setVarKind(clock_var, VariableKind.CLOCKED()); | |
| 257 | end if; | ||
| 258 | else | ||
| 259 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for " + ComponentRef.toString(clock_name) + "."}); | |
| 260 | ✗ | fail(); | |
| 261 | end try; | ||
| 262 | end create; | ||
| 263 | |||
| 264 | function fromExp | ||
| 265 | input Expression exp; | ||
| 266 | output BClock subClock; | ||
| 267 | output Option<ComponentRef> baseClock; | ||
| 268 | algorithm | ||
| 269 | (subClock, baseClock) := match exp | ||
| 270 | local | ||
| 271 | Call call; | ||
| 272 | |||
| 273 | case Expression.CLKCONST() algorithm | ||
| 274 | 12 | then (BASE_CLOCK(exp.clk), NONE()); | |
| 275 | |||
| 276 | case Expression.CREF() algorithm | ||
| 277 | 15 | then (DEFAULT_SUB_CLOCK, SOME(exp.cref)); | |
| 278 | |||
| 279 | case Expression.CALL(call = call as Call.TYPED_CALL()) algorithm | ||
| 280 | (baseClock, subClock) := match (AbsynUtil.pathString(Function.nameConsiderBuiltin(call.fn)), Call.arguments(call)) | ||
| 281 | local | ||
| 282 | Expression e; | ||
| 283 | Integer i1, i2; | ||
| 284 | |||
| 285 | // sample: default subclock sampling | ||
| 286 | case ("sample", {_, e}) algorithm | ||
| 287 | 6 | (subClock, baseClock) := fromExp(e); | |
| 288 | 6 | then (baseClock, subClock); | |
| 289 | |||
| 290 | // subclock: subset sampling | ||
| 291 | case ("subSample", {e, Expression.INTEGER(i1)}) algorithm | ||
| 292 | 5 | (subClock, baseClock) := fromExp(e); | |
| 293 | 5 | subClock := updateSubClock(subClock, SUB_CLOCK(Rational.RATIONAL(i1, 1), Rational.ZERO, NONE())); | |
| 294 | 5 | then (baseClock, subClock); | |
| 295 | |||
| 296 | // subclock: super sampling | ||
| 297 | case ("superSample", {e, Expression.INTEGER(i1)}) algorithm | ||
| 298 | 2 | (subClock, baseClock) := fromExp(e); | |
| 299 | 2 | subClock := updateSubClock(subClock, SUB_CLOCK(Rational.RATIONAL(1, i1), Rational.ZERO, NONE())); | |
| 300 | 2 | then (baseClock, subClock); | |
| 301 | |||
| 302 | // subclock: shift sampling (default 3rd argument = 1) | ||
| 303 | case ("shiftSample", {e, Expression.INTEGER(i1)}) algorithm | ||
| 304 | ✗ | (subClock, baseClock) := fromExp(e); | |
| 305 | ✗ | subClock := updateSubClock(subClock, SUB_CLOCK(Rational.ONE, Rational.RATIONAL(i1, 1), NONE())); | |
| 306 | ✗ | then (baseClock, subClock); | |
| 307 | |||
| 308 | // subclock: shift sampling | ||
| 309 | case ("shiftSample", {e, Expression.INTEGER(i1), Expression.INTEGER(i2)}) algorithm | ||
| 310 | 2 | (subClock, baseClock) := fromExp(e); | |
| 311 | 2 | subClock := updateSubClock(subClock, SUB_CLOCK(Rational.ONE, Rational.RATIONAL(i1, i2), NONE())); | |
| 312 | 2 | then (baseClock, subClock); | |
| 313 | |||
| 314 | // subclock: back sampling (default 3rd argument = 1) | ||
| 315 | case ("backSample", {e, Expression.INTEGER(i1)}) algorithm | ||
| 316 | ✗ | (subClock, baseClock) := fromExp(e); | |
| 317 | ✗ | subClock := updateSubClock(subClock, SUB_CLOCK(Rational.ONE, Rational.RATIONAL(-i1, 1), NONE())); | |
| 318 | ✗ | then (baseClock, subClock); | |
| 319 | |||
| 320 | // subclock: back sampling | ||
| 321 | case ("backSample", {e, Expression.INTEGER(i1), Expression.INTEGER(i2)}) algorithm | ||
| 322 | 1 | (subClock, baseClock) := fromExp(e); | |
| 323 | 1 | subClock := updateSubClock(subClock, SUB_CLOCK(Rational.ONE, Rational.RATIONAL(-i1, i2), NONE())); | |
| 324 | 1 | then (baseClock, subClock); | |
| 325 | |||
| 326 | else algorithm | ||
| 327 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for exp with unhandled call: " + Expression.toString(exp) + "."}); | |
| 328 | ✗ | then fail(); | |
| 329 | end match; | ||
| 330 | then (subClock, baseClock); | ||
| 331 | |||
| 332 | else algorithm | ||
| 333 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for exp with unhandled expression kind: " + Expression.toString(exp) + "."}); | |
| 334 | ✗ | then fail(); | |
| 335 | end match; | ||
| 336 | end fromExp; | ||
| 337 | |||
| 338 | public | ||
| 339 | function updateSubClock | ||
| 340 | "adding the sub clock src to the sub clock dest. not symmetrical/commutative" | ||
| 341 | input output BClock dest; | ||
| 342 | input BClock src; | ||
| 343 | algorithm | ||
| 344 | dest := match (dest, src) | ||
| 345 | case (SUB_CLOCK(), SUB_CLOCK()) algorithm | ||
| 346 | 18 | dest.shift := Rational.add(dest.shift, Rational.mul(src.shift, dest.factor)); | |
| 347 | 18 | dest.factor := Rational.mul(dest.factor, src.factor); | |
| 348 | then dest; | ||
| 349 | else algorithm | ||
| 350 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for " + toString(dest) + " and " + toString(src) + " because of incorrect clock types."}); | |
| 351 | ✗ | then fail(); | |
| 352 | end match; | ||
| 353 | end updateSubClock; | ||
| 354 | end BClock; | ||
| 355 | |||
| 356 | constant BClock DEFAULT_BASE_CLOCK = BASE_CLOCK(ClockKind.REAL_CLOCK(Expression.REAL(1.0))); | ||
| 357 | constant BClock DEFAULT_SUB_CLOCK = SUB_CLOCK(Rational.ONE, Rational.ZERO, NONE()); | ||
| 358 | type CrefLst = list<ComponentRef>; | ||
| 359 | |||
| 360 | uniontype ClockedInfo | ||
| 361 | record CLOCKED_INFO | ||
| 362 | UnorderedMap<ComponentRef, BClock> baseClocks; | ||
| 363 | UnorderedMap<ComponentRef, BClock> subClocks; | ||
| 364 | UnorderedMap<ComponentRef, ComponentRef> subToBase; | ||
| 365 | UnorderedMap<ComponentRef, CrefLst> baseToSub; | ||
| 366 | end CLOCKED_INFO; | ||
| 367 | |||
| 368 | function new | ||
| 369 | output ClockedInfo info = CLOCKED_INFO( | ||
| 370 | baseClocks = UnorderedMap.new<BClock>(ComponentRef.hash, ComponentRef.isEqual), | ||
| 371 | subClocks = UnorderedMap.new<BClock>(ComponentRef.hash, ComponentRef.isEqual), | ||
| 372 | subToBase = UnorderedMap.new<ComponentRef>(ComponentRef.hash, ComponentRef.isEqual), | ||
| 373 | baseToSub = UnorderedMap.new<CrefLst>(ComponentRef.hash, ComponentRef.isEqual)); | ||
| 374 | end new; | ||
| 375 | |||
| 376 | function toString | ||
| 377 | input ClockedInfo info; | ||
| 378 | output String str = ""; | ||
| 379 | algorithm | ||
| 380 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | if not isEmpty(info) then |
| 381 | ✗ | str := StringUtil.headline_2("Clocked Info") + "\n"; | |
| 382 | ✗ | str := str + StringUtil.headline_3("Base Clocks") + UnorderedMap.toString(info.baseClocks, ComponentRef.toString, BClock.toString) + "\n\n"; | |
| 383 | ✗ | str := str + StringUtil.headline_3("Sub Clocks") + UnorderedMap.toString(info.subClocks, ComponentRef.toString, BClock.toString) + "\n\n"; | |
| 384 | ✗ | str := str + StringUtil.headline_3("Sub to Base Clocks") + UnorderedMap.toString(info.subToBase, ComponentRef.toString, ComponentRef.toString) + "\n\n"; | |
| 385 | ✗ | str := str + StringUtil.headline_3("Base to Sub Clocks") + UnorderedMap.toString(info.baseToSub, ComponentRef.toString, ComponentRef.listToString) + "\n"; | |
| 386 | end if; | ||
| 387 | end toString; | ||
| 388 | |||
| 389 | function isEmpty | ||
| 390 | input ClockedInfo info; | ||
| 391 | output Boolean b = UnorderedMap.isEmpty(info.baseClocks); | ||
| 392 | end isEmpty; | ||
| 393 | |||
| 394 | function resolveSubClocks | ||
| 395 | input ClockedInfo info; | ||
| 396 | input UnorderedMap<ComponentRef, ComponentRef> clock_map; | ||
| 397 | algorithm | ||
| 398 | // resolve the implicit clock map | ||
| 399 |
2/2✓ Branch 1 taken 24 times.
✓ Branch 2 taken 190 times.
|
214 | for cref in UnorderedMap.keyList(clock_map) loop |
| 400 | 24 | resolveImplicitSubClock(cref, info, clock_map); | |
| 401 | end for; | ||
| 402 | |||
| 403 | // update sub to base clock | ||
| 404 |
2/2✓ Branch 1 taken 15 times.
✓ Branch 2 taken 190 times.
|
205 | for sub_clock in UnorderedMap.keyList(info.subClocks) loop |
| 405 | 15 | resolveSubClock(sub_clock, info, clock_map); | |
| 406 | end for; | ||
| 407 | |||
| 408 | // update base to sub clocks | ||
| 409 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 190 times.
|
206 | for sub_clock in UnorderedMap.keyList(info.subClocks) loop |
| 410 | 16 | addSubClock(sub_clock, info); | |
| 411 | end for; | ||
| 412 | end resolveSubClocks; | ||
| 413 | |||
| 414 | function baseClockCount | ||
| 415 | input ClockedInfo info; | ||
| 416 | input Boolean countInferred = false; | ||
| 417 | output Integer count = UnorderedMap.size(info.baseClocks); | ||
| 418 | algorithm | ||
| 419 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
|
188 | if not countInferred then |
| 420 | 188 | count := count - List.count(UnorderedMap.valueList(info.baseClocks), BClock.isInferredClock); | |
| 421 | end if; | ||
| 422 | end baseClockCount; | ||
| 423 | |||
| 424 | function subClockCount | ||
| 425 | input ClockedInfo info; | ||
| 426 | output Integer count = UnorderedMap.size(info.subClocks); | ||
| 427 | end subClockCount; | ||
| 428 | |||
| 429 | protected | ||
| 430 | function resolveImplicitSubClock | ||
| 431 | "implicite sub clocks are signals that are clocked but not defined by a sampling function themselves. | ||
| 432 | they infer their clock by the partition they are in. this function resolves each implicit clock to it's | ||
| 433 | root clock that has a sample function definition" | ||
| 434 | input ComponentRef key; | ||
| 435 | input ClockedInfo info; | ||
| 436 | input UnorderedMap<ComponentRef, ComponentRef> clock_map; | ||
| 437 | output ComponentRef clock = key; | ||
| 438 | algorithm | ||
| 439 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if UnorderedMap.contains(key, clock_map) then |
| 440 | 24 | clock := UnorderedMap.getSafe(key, clock_map, sourceInfo()); | |
| 441 |
3/4✓ Branch 1 taken 16 times.
✓ Branch 2 taken 8 times.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
24 | if not (UnorderedMap.contains(clock, info.subClocks) or UnorderedMap.contains(clock, info.baseClocks)) then |
| 442 | ✗ | clock := resolveImplicitSubClock(clock, info, clock_map); | |
| 443 | ✗ | UnorderedMap.add(key, clock, clock_map); | |
| 444 | end if; | ||
| 445 | end if; | ||
| 446 | end resolveImplicitSubClock; | ||
| 447 | |||
| 448 | function resolveSubClock | ||
| 449 | input ComponentRef clock_name; | ||
| 450 | input ClockedInfo info; | ||
| 451 | input UnorderedMap<ComponentRef, ComponentRef> clock_map; | ||
| 452 | output ComponentRef base_clock; | ||
| 453 | protected | ||
| 454 | ComponentRef implicit_clock, parent_clock = UnorderedMap.getSafe(clock_name, info.subToBase, sourceInfo()); | ||
| 455 | Option<ComponentRef> implicit_clock_opt = NONE(); | ||
| 456 | BClock dest, src; | ||
| 457 | algorithm | ||
| 458 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 15 times.
|
23 | if UnorderedMap.contains(parent_clock, info.baseClocks) then |
| 459 | // just a base clock | ||
| 460 | base_clock := parent_clock; | ||
| 461 | else | ||
| 462 | // not a base, update necessary | ||
| 463 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 7 times.
|
8 | if not UnorderedMap.contains(parent_clock, info.subClocks) then |
| 464 | // neither base nor sub clock --> implicit clock. map it | ||
| 465 | implicit_clock_opt := SOME(parent_clock); | ||
| 466 | 1 | parent_clock := UnorderedMap.getSafe(parent_clock, clock_map, sourceInfo()); | |
| 467 | end if; | ||
| 468 | 8 | base_clock := resolveSubClock(parent_clock, info, clock_map); | |
| 469 | |||
| 470 | // update the sub clock and add the new base clock | ||
| 471 | 8 | dest := UnorderedMap.getSafe(parent_clock, info.subClocks, sourceInfo()); | |
| 472 | 8 | src := UnorderedMap.getSafe(clock_name, info.subClocks, sourceInfo()); | |
| 473 | 8 | UnorderedMap.add(clock_name, BClock.updateSubClock(dest, src), info.subClocks); | |
| 474 | 8 | UnorderedMap.add(clock_name, base_clock, info.subToBase); | |
| 475 | |||
| 476 | // also update and add the implicit clock | ||
| 477 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 7 times.
|
8 | if isSome(implicit_clock_opt) then |
| 478 | 1 | SOME(implicit_clock) := implicit_clock_opt; | |
| 479 | // add the implicit clock as a sub clock | ||
| 480 | 1 | UnorderedMap.add(implicit_clock, dest, info.subClocks); | |
| 481 | 1 | UnorderedMap.add(implicit_clock, base_clock, info.subToBase); | |
| 482 | end if; | ||
| 483 | end if; | ||
| 484 | end resolveSubClock; | ||
| 485 | |||
| 486 | function addSubClock | ||
| 487 | input ComponentRef clock_name; | ||
| 488 | input ClockedInfo info; | ||
| 489 | protected | ||
| 490 | ComponentRef base_clock = UnorderedMap.getSafe(clock_name, info.subToBase, sourceInfo()); | ||
| 491 | List<ComponentRef> current_clocks; | ||
| 492 | algorithm | ||
| 493 | 16 | current_clocks := UnorderedMap.getOrDefault(base_clock, info.baseToSub, {}); | |
| 494 | 16 | UnorderedMap.add(base_clock, clock_name :: current_clocks, info.baseToSub); | |
| 495 | end addSubClock; | ||
| 496 | end ClockedInfo; | ||
| 497 | |||
| 498 | // ========================================================================= | ||
| 499 | // MAIN ROUTINE, PLEASE DO NOT CHANGE | ||
| 500 | // ========================================================================= | ||
| 501 | function main | ||
| 502 | "Wrapper function for any partitioning function. This will be | ||
| 503 | called during simulation and gets the corresponding subfunction from | ||
| 504 | Config." | ||
| 505 | extends Module.wrapper; | ||
| 506 | input Partition.Kind kind; | ||
| 507 | protected | ||
| 508 | Module.partitioningInterface func; | ||
| 509 | algorithm | ||
| 510 | 378 | func := getModule(); | |
| 511 | |||
| 512 | bdae := match (kind, bdae) | ||
| 513 | local | ||
| 514 | VariablePointers variables, clocks; | ||
| 515 | EquationPointers equations, clocked; | ||
| 516 | |||
| 517 | case (NBPartition.Kind.ODE, BackendDAE.MAIN( | ||
| 518 | varData = BVariable.VAR_DATA_SIM(unknowns = variables, clocks = clocks), | ||
| 519 | eqData = BEquation.EQ_DATA_SIM(simulation = equations, clocked = clocked))) | ||
| 520 | algorithm | ||
| 521 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 190 times.
|
190 | bdae.ode := func(kind, variables, equations, clocks, clocked, bdae.clockedInfo); |
| 522 |
5/6✗ Branch 1 not taken.
✓ Branch 2 taken 329 times.
✓ Branch 3 taken 329 times.
✓ Branch 4 taken 190 times.
✓ Branch 5 taken 329 times.
✓ Branch 6 taken 190 times.
|
709 | bdae.ode := list(sys for sys guard(not Partition.Partition.isEmpty(sys)) in bdae.ode); |
| 523 | // remove all inferred clocks from discrete vars and equations after partitioning | ||
| 524 | 190 | bdae.varData := VarData.removeTypedCheck(bdae.varData, BVariable.isClock, VarData.VarType.DISCRETE); | |
| 525 | 190 | bdae.eqData := EqData.removeTypedCheck(bdae.eqData, Equation.isTypeClock, EqData.EqType.DISCRETE); | |
| 526 | then bdae; | ||
| 527 | |||
| 528 | case (_, BackendDAE.MAIN( | ||
| 529 | varData = BVariable.VAR_DATA_SIM(initials = variables, clocks = clocks), | ||
| 530 | eqData = BEquation.EQ_DATA_SIM(initials = equations, clocked = clocked))) | ||
| 531 | guard(Partition.kindIsInitial(kind)) | ||
| 532 | algorithm | ||
| 533 | 188 | bdae.init := partitioningNone(kind, variables, equations, clocks, clocked, bdae.clockedInfo); | |
| 534 |
6/6✓ Branch 1 taken 13 times.
✓ Branch 2 taken 175 times.
✓ Branch 3 taken 188 times.
✓ Branch 4 taken 188 times.
✓ Branch 5 taken 175 times.
✓ Branch 6 taken 188 times.
|
564 | bdae.init := list(sys for sys guard(not Partition.Partition.isEmpty(sys)) in bdae.init); |
| 535 | then bdae; | ||
| 536 | |||
| 537 | else algorithm | ||
| 538 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."}); | |
| 539 | ✗ | then fail(); | |
| 540 | end match; | ||
| 541 | end main; | ||
| 542 | |||
| 543 | function getModule | ||
| 544 | "Returns the module function that was chosen by the user." | ||
| 545 | output Module.partitioningInterface func; | ||
| 546 | protected | ||
| 547 | String flag = "clocked"; //Flags.getConfigString(Flags.PARTITIONING) | ||
| 548 | algorithm | ||
| 549 | func := match flag | ||
| 550 | case "default" then (partitioningClocked); | ||
| 551 | case "clocked" then (partitioningClocked); | ||
| 552 | case "none" then (partitioningNone); | ||
| 553 | /* ... New detect states modules have to be added here */ | ||
| 554 | else fail(); | ||
| 555 | end match; | ||
| 556 | end getModule; | ||
| 557 | |||
| 558 | function categorize | ||
| 559 | "creates ODE, ALG, ODE_EVT, ALG_EVT partitions from ODE by checking | ||
| 560 | if it contains discrete equations or state equations. | ||
| 561 | Should be evoked just before jacobian at the very end." | ||
| 562 | extends Module.wrapper; | ||
| 563 | algorithm | ||
| 564 | bdae := match bdae | ||
| 565 | local | ||
| 566 | DoubleEnded.MutableList<Partition.Partition> ode = DoubleEnded.MutableList.fromList({}); | ||
| 567 | DoubleEnded.MutableList<Partition.Partition> alg = DoubleEnded.MutableList.fromList({}); | ||
| 568 | DoubleEnded.MutableList<Partition.Partition> ode_evt = DoubleEnded.MutableList.fromList({}); | ||
| 569 | DoubleEnded.MutableList<Partition.Partition> alg_evt = DoubleEnded.MutableList.fromList({}); | ||
| 570 | DoubleEnded.MutableList<Partition.Partition> clocked = DoubleEnded.MutableList.fromList({}); | ||
| 571 | |||
| 572 | case BackendDAE.MAIN() algorithm | ||
| 573 |
2/2✓ Branch 0 taken 327 times.
✓ Branch 1 taken 188 times.
|
515 | for syst in bdae.ode loop |
| 574 | 327 | Partition.Partition.categorize(syst, ode, alg, ode_evt, alg_evt, clocked); | |
| 575 | end for; | ||
| 576 | 188 | bdae.ode := DoubleEnded.MutableList.toListAndClear(ode); | |
| 577 | 188 | bdae.algebraic := DoubleEnded.MutableList.toListAndClear(alg); | |
| 578 | 188 | bdae.ode_event := DoubleEnded.MutableList.toListAndClear(ode_evt); | |
| 579 | 188 | bdae.alg_event := DoubleEnded.MutableList.toListAndClear(alg_evt); | |
| 580 | 188 | bdae.clocked := DoubleEnded.MutableList.toListAndClear(clocked); | |
| 581 | then bdae; | ||
| 582 | |||
| 583 | else algorithm | ||
| 584 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."}); | |
| 585 | ✗ | then fail(); | |
| 586 | end match; | ||
| 587 | end categorize; | ||
| 588 | |||
| 589 | function extractClocksEqn | ||
| 590 | input output Equation eqn; | ||
| 591 | input UnorderedMap<BClock, ComponentRef> clck_coll; | ||
| 592 | input UnorderedMap<BClock, ComponentRef> infr_coll; | ||
| 593 | input Pointer<list<Pointer<Variable>>> new_clocks; | ||
| 594 | input Pointer<list<Pointer<Variable>>> new_infers; | ||
| 595 | input Pointer<Integer> idx; | ||
| 596 | algorithm | ||
| 597 | eqn := match eqn | ||
| 598 | case Equation.WHEN_EQUATION() algorithm | ||
| 599 | 67 | eqn.body := Util.getOption(extractClocksWhenCond(SOME(eqn.body), clck_coll, infr_coll, new_clocks, new_infers, idx)); | |
| 600 | then eqn; | ||
| 601 | else eqn; | ||
| 602 | end match; | ||
| 603 | 4145 | eqn := Equation.map(eqn, function extractClocks(clck_coll = clck_coll, infr_coll = infr_coll, new_clocks = new_clocks, new_infers = new_infers, idx = idx, when_cond = false)); | |
| 604 | end extractClocksEqn; | ||
| 605 | |||
| 606 | function extractClocksWhenCond | ||
| 607 | input output Option<WhenEquationBody> body_opt; | ||
| 608 | input UnorderedMap<BClock, ComponentRef> clck_coll; | ||
| 609 | input UnorderedMap<BClock, ComponentRef> infr_coll; | ||
| 610 | input Pointer<list<Pointer<Variable>>> new_clocks; | ||
| 611 | input Pointer<list<Pointer<Variable>>> new_infers; | ||
| 612 | input Pointer<Integer> idx; | ||
| 613 | algorithm | ||
| 614 | body_opt := match body_opt | ||
| 615 | local | ||
| 616 | WhenEquationBody body; | ||
| 617 | case SOME(body) algorithm | ||
| 618 | 234 | body.condition := Expression.map(body.condition, function extractClocks(clck_coll = clck_coll, infr_coll = infr_coll, new_clocks = new_clocks, new_infers = new_infers, idx = idx, when_cond = true)); | |
| 619 | 117 | body.else_when := extractClocksWhenCond(body.else_when, clck_coll, infr_coll, new_clocks, new_infers, idx); | |
| 620 | then SOME(body); | ||
| 621 | else body_opt; | ||
| 622 | end match; | ||
| 623 | end extractClocksWhenCond; | ||
| 624 | |||
| 625 | function extractClocks | ||
| 626 | "replace clock constructors in expressions with variables" | ||
| 627 | input output Expression exp; | ||
| 628 | input UnorderedMap<BClock, ComponentRef> clck_coll; | ||
| 629 | input UnorderedMap<BClock, ComponentRef> infr_coll; | ||
| 630 | input Pointer<list<Pointer<Variable>>> new_clocks; | ||
| 631 | input Pointer<list<Pointer<Variable>>> new_infers; | ||
| 632 | input Pointer<Integer> idx; | ||
| 633 | input Boolean when_cond; | ||
| 634 | algorithm | ||
| 635 | exp := match exp | ||
| 636 | local | ||
| 637 | BClock clock; | ||
| 638 | Pointer<Variable> clock_var; | ||
| 639 | ComponentRef clock_name; | ||
| 640 | |||
| 641 | case Expression.CLKCONST() guard(when_cond or not ClockKind.isInferred(exp.clk)) algorithm | ||
| 642 | 2 | clock := BClock.BASE_CLOCK(exp.clk); | |
| 643 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if UnorderedMap.contains(clock, clck_coll) then |
| 644 | // clock already exists | ||
| 645 | ✗ | clock_name := UnorderedMap.getSafe(clock, clck_coll, sourceInfo()); | |
| 646 | elseif UnorderedMap.contains(clock, infr_coll) then | ||
| 647 | ✗ | clock_name := UnorderedMap.getSafe(clock, infr_coll, sourceInfo()); | |
| 648 | else | ||
| 649 | // new clock | ||
| 650 | 2 | (clock_var, clock_name) := BVariable.makeClockVar(Pointer.access(idx), Expression.typeOf(exp)); | |
| 651 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if BClock.isInferredClock(clock) then |
| 652 | ✗ | UnorderedMap.add(clock, clock_name, infr_coll); | |
| 653 | ✗ | Pointer.update(new_infers, clock_var :: Pointer.access(new_infers)); | |
| 654 | else | ||
| 655 | 2 | UnorderedMap.add(clock, clock_name, clck_coll); | |
| 656 | 4 | Pointer.update(new_clocks, clock_var :: Pointer.access(new_clocks)); | |
| 657 | end if; | ||
| 658 | 2 | Pointer.update(idx, Pointer.access(idx) + 1); | |
| 659 | end if; | ||
| 660 | 2 | then Expression.fromCref(clock_name); | |
| 661 | |||
| 662 | else exp; | ||
| 663 | end match; | ||
| 664 | end extractClocks; | ||
| 665 | |||
| 666 | protected | ||
| 667 | type ClusterElementType = enumeration(EQUATION, VARIABLE); | ||
| 668 | |||
| 669 | uniontype Cluster | ||
| 670 | record CLUSTER | ||
| 671 | UnorderedSet<ComponentRef> variables "set of all variables in this cluster"; | ||
| 672 | UnorderedSet<ComponentRef> eqn_idnts "set of all equations in this cluster"; | ||
| 673 | end CLUSTER; | ||
| 674 | |||
| 675 | function toString | ||
| 676 | input Cluster cluster; | ||
| 677 | output String str; | ||
| 678 | algorithm | ||
| 679 | ✗ | str := "### Cluster Variables:\n" + UnorderedSet.toString(cluster.variables, ComponentRef.toString) | |
| 680 | + "\n### Cluster Equation Identifiers:\n" + UnorderedSet.toString(cluster.eqn_idnts, ComponentRef.toString); | ||
| 681 | end toString; | ||
| 682 | |||
| 683 | function addElement | ||
| 684 | input Option<Cluster> cluster_opt; | ||
| 685 | input ComponentRef cref; | ||
| 686 | input ClusterElementType ty; | ||
| 687 | output Cluster cluster; | ||
| 688 | algorithm | ||
| 689 | cluster := match cluster_opt | ||
| 690 | case SOME(cluster) then cluster; | ||
| 691 | 330 | else CLUSTER( | |
| 692 | variables = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual), | ||
| 693 | eqn_idnts = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual)); | ||
| 694 | end match; | ||
| 695 | |||
| 696 | cluster := match ty | ||
| 697 | case ClusterElementType.VARIABLE algorithm | ||
| 698 | 2554 | UnorderedSet.add(cref, cluster.variables); | |
| 699 | then cluster; | ||
| 700 | case ClusterElementType.EQUATION algorithm | ||
| 701 | 3062 | UnorderedSet.add(cref, cluster.eqn_idnts); | |
| 702 | then cluster; | ||
| 703 | else algorithm | ||
| 704 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed for " + ComponentRef.toString(cref) + " because of unknown cluster element type."}); | |
| 705 | ✗ | then fail(); | |
| 706 | end match; | ||
| 707 | end addElement; | ||
| 708 | |||
| 709 | function addToClockMap | ||
| 710 | "finds the first clock in the map and adds cref->clock for all crefs in this cluster. | ||
| 711 | Note: does not check if there are different clocks that contradict, this check will be done later. | ||
| 712 | this is only for naive clock inference that will be checked for consistency later." | ||
| 713 | input Cluster cluster; | ||
| 714 | input EquationPointers equations; | ||
| 715 | input ClockedInfo info; | ||
| 716 | input UnorderedMap<ComponentRef, ComponentRef> clock_map; | ||
| 717 | protected | ||
| 718 | function findClock | ||
| 719 | "finds the first clock/clocked signal and skips everything afterwards" | ||
| 720 | input output Expression exp; | ||
| 721 | input ClockedInfo info; | ||
| 722 | input Pointer<Option<ComponentRef>> clock_ptr; | ||
| 723 | protected | ||
| 724 | Option<ComponentRef> clock_opt = Pointer.access(clock_ptr); | ||
| 725 | algorithm | ||
| 726 | exp := match (exp, clock_opt) | ||
| 727 | // already found clock, do nothing | ||
| 728 | case (_, SOME(_)) then exp; | ||
| 729 | |||
| 730 | case (Expression.CREF(), NONE()) guard(BVariable.isClockOrClocked(BVariable.getVarPointer(exp.cref, sourceInfo()))) algorithm | ||
| 731 | // add the clock cref | ||
| 732 | 19 | Pointer.update(clock_ptr, SOME(exp.cref)); | |
| 733 | then exp; | ||
| 734 | |||
| 735 | // do nothing on clock sampling functions as they do not imply a clock for this cluster | ||
| 736 | case (Expression.CALL(), _) guard(Expression.isClockOrSampleFunction(exp)) then exp; | ||
| 737 | |||
| 738 | // go deeper | ||
| 739 | 21308 | else Expression.mapShallow(exp, function findClock(info = info, clock_ptr = clock_ptr)); | |
| 740 | end match; | ||
| 741 | end findClock; | ||
| 742 | Pointer<Option<ComponentRef>> clock_ptr = Pointer.create(NONE()); | ||
| 743 | Option<ComponentRef> clock_opt = NONE(); | ||
| 744 | ComponentRef clock; | ||
| 745 | algorithm | ||
| 746 | // search all equations until first clock/clocked signal is found | ||
| 747 |
2/2✓ Branch 1 taken 3058 times.
✓ Branch 2 taken 311 times.
|
3369 | for eqn_name in UnorderedSet.toList(cluster.eqn_idnts) loop |
| 748 | 3058 | Equation.map(Pointer.access(EquationPointers.getEqnByName(equations, eqn_name)), function findClock(info = info, clock_ptr = clock_ptr), NONE(), Expression.fakeMap); | |
| 749 | 3058 | clock_opt := Pointer.access(clock_ptr); | |
| 750 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 3058 times.
✓ Branch 2 taken 3039 times.
✓ Branch 3 taken 19 times.
|
3058 | if isSome(clock_opt) then break; end if; |
| 751 | end for; | ||
| 752 | |||
| 753 | // if a clock/clocked signal was found, add all a mapping for each variable in the cluster to the clock | ||
| 754 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 311 times.
|
330 | if isSome(clock_opt) then |
| 755 | 19 | SOME(clock) := clock_opt; | |
| 756 |
2/2✓ Branch 1 taken 24 times.
✓ Branch 2 taken 19 times.
|
43 | for var_name in UnorderedSet.toList(cluster.variables) loop |
| 757 | 24 | UnorderedMap.add(var_name, clock, clock_map); | |
| 758 | end for; | ||
| 759 | end if; | ||
| 760 | end addToClockMap; | ||
| 761 | |||
| 762 | function toPartition | ||
| 763 | input Cluster cluster; | ||
| 764 | input VariablePointers variables; | ||
| 765 | input EquationPointers equations; | ||
| 766 | input Partition.Kind kind; | ||
| 767 | input ClockedInfo info; | ||
| 768 | input UnorderedSet<ComponentRef> held_crefs; | ||
| 769 | input UnorderedSet<ComponentRef> infer_del; | ||
| 770 | output Partition.Partition partition; | ||
| 771 | protected | ||
| 772 | list<ComponentRef> cvars = UnorderedSet.toList(cluster.variables); | ||
| 773 | list<ComponentRef> cidnt = UnorderedSet.toList(cluster.eqn_idnts); | ||
| 774 | Partition.Association association; | ||
| 775 | list<Pointer<Variable>> var_lst, filtered_vars; | ||
| 776 | list<Pointer<Equation>> eqn_lst; | ||
| 777 | VariablePointers partVariables; | ||
| 778 | EquationPointers partEquations; | ||
| 779 | UnorderedSet<ComponentRef> inferred_clocks = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual); | ||
| 780 | algorithm | ||
| 781 | // find all variables and equations | ||
| 782 |
4/4✓ Branch 0 taken 2554 times.
✓ Branch 1 taken 330 times.
✓ Branch 2 taken 2554 times.
✓ Branch 3 taken 330 times.
|
2884 | var_lst := list(BVariable.getVarPointer(cref, sourceInfo()) for cref in cvars); |
| 783 |
5/6✗ Branch 1 not taken.
✓ Branch 2 taken 2554 times.
✓ Branch 3 taken 2554 times.
✓ Branch 4 taken 330 times.
✓ Branch 5 taken 2554 times.
✓ Branch 6 taken 330 times.
|
2884 | filtered_vars := list(var for var guard(VariablePointers.contains(var, variables)) in var_lst); |
| 784 |
4/4✓ Branch 0 taken 3062 times.
✓ Branch 1 taken 330 times.
✓ Branch 2 taken 3062 times.
✓ Branch 3 taken 330 times.
|
3392 | eqn_lst := list(EquationPointers.getEqnByName(equations, name) for name in cidnt); |
| 785 | |||
| 786 | // create variable and equation arrays | ||
| 787 | 330 | partVariables := VariablePointers.fromList(filtered_vars); | |
| 788 | 330 | partEquations := EquationPointers.fromList(eqn_lst); | |
| 789 | |||
| 790 | // create the association (clocked/continuous) | ||
| 791 | 330 | association := Partition.Association.create(partEquations, kind, info, infer_del); | |
| 792 | |||
| 793 | // replace the clocked functions, inline clocked when equations and set equations to clocked | ||
| 794 | 330 | partEquations := EquationPointers.mapExp(partEquations, function replaceClockedFunctions(held_crefs = held_crefs)); | |
| 795 |
2/2✓ Branch 1 taken 19 times.
✓ Branch 2 taken 311 times.
|
330 | if Partition.Association.isClocked(association) then |
| 796 | // remove the inferred clocks | ||
| 797 | 19 | partVariables := VariablePointers.mapRemovePtr(partVariables, function collectInferredClock(inferred_clocks = inferred_clocks)); | |
| 798 | 19 | partEquations := EquationPointers.mapRemovePtr(partEquations, function removeInferredClock(inferred_clocks = inferred_clocks)); | |
| 799 | // replace clocked when equations and make all variables clocked | ||
| 800 | 19 | partEquations := EquationPointers.map(partEquations, replaceClockedWhen); | |
| 801 | 19 | partVariables := VariablePointers.mapPtr(partVariables, function BVariable.setVarKind(varKind = VariableKind.CLOCKED())); | |
| 802 | |||
| 803 | // if the partition will be removed add all unused inferred clocks | ||
| 804 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
|
19 | if EquationPointers.size(partEquations) == 0 then |
| 805 | ✗ | UnorderedSet.merge(infer_del, inferred_clocks); | |
| 806 | end if; | ||
| 807 | end if; | ||
| 808 | |||
| 809 | 330 | partition := Partition.PARTITION( | |
| 810 | index = 0, | ||
| 811 | association = association, | ||
| 812 | unknowns = partVariables, | ||
| 813 | daeUnknowns = NONE(), | ||
| 814 | equations = partEquations, | ||
| 815 | adjacencyMatrix = NONE(), | ||
| 816 | matching = NONE(), | ||
| 817 | strongComponents = NONE() | ||
| 818 | ); | ||
| 819 | end toPartition; | ||
| 820 | |||
| 821 | protected | ||
| 822 | function collectInferredClock | ||
| 823 | input Pointer<Variable> var; | ||
| 824 | input UnorderedSet<ComponentRef> inferred_clocks; | ||
| 825 | output Boolean delete = BVariable.isClock(var); | ||
| 826 | algorithm | ||
| 827 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | if delete then |
| 828 | ✗ | UnorderedSet.add(BVariable.getVarName(var), inferred_clocks); | |
| 829 | end if; | ||
| 830 | end collectInferredClock; | ||
| 831 | |||
| 832 | function removeInferredClock | ||
| 833 | input Pointer<Equation> eqn; | ||
| 834 | input UnorderedSet<ComponentRef> inferred_clocks; | ||
| 835 | output Boolean delete; | ||
| 836 | algorithm | ||
| 837 | delete := match Pointer.access(eqn) | ||
| 838 | local | ||
| 839 | ComponentRef lhs; | ||
| 840 | 16 | case Equation.SCALAR_EQUATION(lhs = Expression.CREF(cref = lhs)) then UnorderedSet.contains(lhs, inferred_clocks); | |
| 841 | else false; | ||
| 842 | end match; | ||
| 843 | end removeInferredClock; | ||
| 844 | end Cluster; | ||
| 845 | |||
| 846 | // Perhaps this deserves its own place in Util/*.mo | ||
| 847 | uniontype DisjointSetForest | ||
| 848 | "Custom implementation of disjoint-set data structure with constant number of elements." | ||
| 849 | record FOREST | ||
| 850 | Pointer<array<Integer>> parent; | ||
| 851 | Pointer<array<Integer>> rank; | ||
| 852 | end FOREST; | ||
| 853 | |||
| 854 | function new | ||
| 855 | "Creates n disjoit subsets of size 1." | ||
| 856 | input Integer n; | ||
| 857 | output DisjointSetForest dsf; | ||
| 858 | algorithm | ||
| 859 |
2/2✓ Branch 0 taken 3062 times.
✓ Branch 1 taken 190 times.
|
3252 | dsf := FOREST( |
| 860 | parent = Pointer.create(listArray(list(i for i in 1:n))), | ||
| 861 | rank = Pointer.create(arrayCreate(n, 0)) | ||
| 862 | ); | ||
| 863 | end new; | ||
| 864 | |||
| 865 | function find | ||
| 866 | input DisjointSetForest dsf; | ||
| 867 | input output Integer index; | ||
| 868 | protected | ||
| 869 | array<Integer> parent = Pointer.access(dsf.parent); | ||
| 870 | algorithm | ||
| 871 |
2/2✓ Branch 1 taken 3587 times.
✓ Branch 2 taken 20046 times.
|
23633 | while index <> parent[index] loop |
| 872 | 3587 | parent[index] := parent[parent[index]] "path halving"; | |
| 873 | 3587 | index := parent[index]; | |
| 874 | end while; | ||
| 875 | 20046 | Pointer.update(dsf.parent, parent); | |
| 876 | end find; | ||
| 877 | |||
| 878 | function unite | ||
| 879 | input DisjointSetForest dsf; | ||
| 880 | input list<Integer> indices; | ||
| 881 | output Integer root; | ||
| 882 | protected | ||
| 883 | list<Integer> roots = list(find(dsf, i) for i in indices); | ||
| 884 | array<Integer> parent = Pointer.access(dsf.parent); | ||
| 885 | array<Integer> rank = Pointer.access(dsf.rank); | ||
| 886 | Integer maxRank; | ||
| 887 | Boolean tied = false; | ||
| 888 | algorithm | ||
| 889 | // find root with highest rank | ||
| 890 | 3062 | root := listHead(roots); | |
| 891 | 3062 | maxRank := rank[root]; | |
| 892 |
2/2✓ Branch 1 taken 4153 times.
✓ Branch 2 taken 3062 times.
|
7215 | for r in listRest(roots) loop |
| 893 |
2/2✓ Branch 0 taken 2804 times.
✓ Branch 1 taken 1349 times.
|
4153 | if r <> root then |
| 894 |
2/2✓ Branch 1 taken 801 times.
✓ Branch 2 taken 2003 times.
|
2804 | if rank[r] > maxRank then |
| 895 | root := r; | ||
| 896 | maxRank := rank[root]; | ||
| 897 | tied := false; | ||
| 898 | elseif rank[r] == maxRank then | ||
| 899 | tied := true; | ||
| 900 | end if; | ||
| 901 | end if; | ||
| 902 | end for; | ||
| 903 | |||
| 904 | // update parents | ||
| 905 |
2/2✓ Branch 1 taken 7215 times.
✓ Branch 2 taken 3062 times.
|
10277 | for r in roots loop |
| 906 | 7215 | parent[find(dsf, r)] := root; | |
| 907 | end for; | ||
| 908 | |||
| 909 | // if necessary increment rank | ||
| 910 |
2/2✓ Branch 0 taken 363 times.
✓ Branch 1 taken 2699 times.
|
3062 | if tied then |
| 911 | 363 | rank[root] := rank[root] + 1; | |
| 912 | end if; | ||
| 913 | |||
| 914 | 3062 | Pointer.update(dsf.parent, parent); | |
| 915 | 3062 | Pointer.update(dsf.rank, rank); | |
| 916 | end unite; | ||
| 917 | end DisjointSetForest; | ||
| 918 | |||
| 919 | function partitioningNone extends Module.partitioningInterface; | ||
| 920 | protected | ||
| 921 | VariablePointers clone_vars; | ||
| 922 | EquationPointers clone_eqns; | ||
| 923 | algorithm | ||
| 924 | 188 | clone_vars := VariablePointers.clone(variables); | |
| 925 | 188 | clone_eqns := EquationPointers.clone(equations); | |
| 926 | 188 | partitions := {Partition.PARTITION( | |
| 927 | index = 1, | ||
| 928 | association = Partition.Association.CONTINUOUS(kind, NONE(), NONE(), NONE(), NONE(), NONE()), | ||
| 929 | unknowns = clone_vars, | ||
| 930 | daeUnknowns = NONE(), | ||
| 931 | equations = clone_eqns, | ||
| 932 | adjacencyMatrix = NONE(), | ||
| 933 | matching = NONE(), | ||
| 934 | strongComponents = NONE() | ||
| 935 | )}; | ||
| 936 | end partitioningNone; | ||
| 937 | |||
| 938 | function partitioningClocked | ||
| 939 | "partitions all individual partitions and collects the clocked partitions and clocks/subclocks" | ||
| 940 | extends Module.partitioningInterface; | ||
| 941 | protected | ||
| 942 | DisjointSetForest eqn_dsf = DisjointSetForest.new(ExpandableArray.getLastUsedIndex(equations.eqArr)); | ||
| 943 | array<Integer> var_map = arrayCreate(ExpandableArray.getLastUsedIndex(variables.varArr), -1); | ||
| 944 | Pointer<Equation> eqn; | ||
| 945 | Pointer<Variable> var; | ||
| 946 | UnorderedSet<ComponentRef> var_crefs; | ||
| 947 | list<Integer> var_indices; | ||
| 948 | Integer part_idx; | ||
| 949 | UnorderedMap<Integer, Cluster> cluster_map = UnorderedMap.new<Cluster>(Util.id, intEq); | ||
| 950 | ComponentRef name_cref; | ||
| 951 | array<Boolean> marked_vars; | ||
| 952 | list<Pointer<Variable>> single_vars; | ||
| 953 | UnorderedSet<ComponentRef> held_crefs = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual); | ||
| 954 | // maps each cref to the clock it is listening to | ||
| 955 | UnorderedMap<ComponentRef, ComponentRef> clock_map = UnorderedMap.new<ComponentRef>(ComponentRef.hash, ComponentRef.isEqual); | ||
| 956 | UnorderedSet<ComponentRef> infer_del = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual); | ||
| 957 | Pointer<Integer> index = Pointer.create(1); | ||
| 958 | algorithm | ||
| 959 | // parse clock assignments | ||
| 960 |
2/2✓ Branch 1 taken 15 times.
✓ Branch 2 taken 190 times.
|
205 | for eq_idx in UnorderedMap.valueList(clocked.map) loop |
| 961 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | if eq_idx > 0 then |
| 962 | 15 | eqn := EquationPointers.getEqnAt(clocked, eq_idx); | |
| 963 | 15 | BClock.add(Pointer.access(eqn), info); | |
| 964 | end if; | ||
| 965 | end for; | ||
| 966 | |||
| 967 | // other equations - collect all variables and check for clocked signals | ||
| 968 |
2/2✓ Branch 1 taken 3062 times.
✓ Branch 2 taken 190 times.
|
3252 | for eq_idx in UnorderedMap.valueList(equations.map) loop |
| 969 |
1/2✓ Branch 0 taken 3062 times.
✗ Branch 1 not taken.
|
3062 | if eq_idx > 0 then |
| 970 | 3062 | eqn := EquationPointers.getEqnAt(equations, eq_idx); | |
| 971 | 3062 | BClock.add(Pointer.access(eqn), info); | |
| 972 | |||
| 973 | // collect all crefs in equation | ||
| 974 | 3062 | var_crefs := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual); | |
| 975 | 3062 | Equation.map(Pointer.access(eqn), function collectPartitioningCrefs(var_crefs = var_crefs), NONE(), Expression.fakeMap); | |
| 976 | |||
| 977 | // find all indices of connected variables | ||
| 978 |
4/4✓ Branch 1 taken 6998 times.
✓ Branch 2 taken 3062 times.
✓ Branch 3 taken 6998 times.
✓ Branch 4 taken 3062 times.
|
10060 | var_indices := list(VariablePointers.getVarIndex(variables, cref) for cref in UnorderedSet.toList(var_crefs)); |
| 979 | // filter indices of non existant variables (e.g. time) | ||
| 980 |
6/6✓ Branch 0 taken 291 times.
✓ Branch 1 taken 6707 times.
✓ Branch 2 taken 6998 times.
✓ Branch 3 taken 3062 times.
✓ Branch 4 taken 6707 times.
✓ Branch 5 taken 3062 times.
|
10060 | var_indices := list(i for i guard(i > 0) in var_indices); |
| 981 | |||
| 982 | // unite current equation and all variables that already belong to a partition | ||
| 983 |
6/6✓ Branch 1 taken 2554 times.
✓ Branch 2 taken 4153 times.
✓ Branch 3 taken 6707 times.
✓ Branch 4 taken 3062 times.
✓ Branch 5 taken 4153 times.
✓ Branch 6 taken 3062 times.
|
9769 | part_idx := DisjointSetForest.unite(eqn_dsf, eq_idx :: list(var_map[j] for j guard(var_map[j] > 0) in var_indices)); |
| 984 | |||
| 985 | // update connected variable partition indices | ||
| 986 |
2/2✓ Branch 1 taken 6707 times.
✓ Branch 2 taken 3062 times.
|
9769 | for i in var_indices loop |
| 987 | 6707 | var_map[i] := part_idx; | |
| 988 | end for; | ||
| 989 | end if; | ||
| 990 | end for; | ||
| 991 | |||
| 992 | // find and report variables that could not be assigned to a partition (exclude clocks) | ||
| 993 |
5/6✓ Branch 1 taken 2554 times.
✓ Branch 2 taken 190 times.
✓ Branch 3 taken 2554 times.
✓ Branch 4 taken 190 times.
✓ Branch 6 taken 2554 times.
✗ Branch 7 not taken.
|
2744 | marked_vars := listArray(list(var_map[var_idx] < 0 for var_idx in UnorderedMap.valueList(variables.map))); |
| 994 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 190 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 190 times.
|
190 | single_vars := list(var_ptr for var_ptr in VariablePointers.getMarkedVars(variables, marked_vars)); |
| 995 | |||
| 996 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 190 times.
|
190 | if not listEmpty(single_vars) then |
| 997 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " (" + Partition.Partition.kindToString(kind) | |
| 998 | + ") failed because the following variables could not be assigned to a partition:\n {" | ||
| 999 | + stringDelimitList(list(BVariable.toString(Pointer.access(var_ptr)) for var_ptr in single_vars), "\n") + "}"}); | ||
| 1000 | ✗ | fail(); | |
| 1001 | end if; | ||
| 1002 | |||
| 1003 | // collect cluster equations | ||
| 1004 |
2/2✓ Branch 1 taken 3062 times.
✓ Branch 2 taken 190 times.
|
3252 | for eq_idx in UnorderedMap.valueList(equations.map) loop |
| 1005 |
1/2✓ Branch 0 taken 3062 times.
✗ Branch 1 not taken.
|
3062 | if eq_idx > 0 then |
| 1006 | // add the equation | ||
| 1007 | 3062 | eqn := EquationPointers.getEqnAt(equations, eq_idx); | |
| 1008 | 3062 | name_cref := Equation.getEqnName(eqn); | |
| 1009 | 3062 | part_idx := DisjointSetForest.find(eqn_dsf, eq_idx); | |
| 1010 | 3062 | UnorderedMap.addUpdate(part_idx, function Cluster.addElement(cref = name_cref, ty = ClusterElementType.EQUATION), cluster_map); | |
| 1011 | end if; | ||
| 1012 | end for; | ||
| 1013 | |||
| 1014 | // collect cluster variables | ||
| 1015 |
2/2✓ Branch 1 taken 2554 times.
✓ Branch 2 taken 190 times.
|
2744 | for var_idx in UnorderedMap.valueList(variables.map) loop |
| 1016 |
1/2✓ Branch 0 taken 2554 times.
✗ Branch 1 not taken.
|
2554 | if var_idx > 0 then |
| 1017 | 2554 | var := VariablePointers.getVarAt(variables, var_idx); | |
| 1018 | 2554 | name_cref := BVariable.getVarName(var); | |
| 1019 | 2554 | part_idx := DisjointSetForest.find(eqn_dsf, var_map[var_idx]); | |
| 1020 | 2554 | UnorderedMap.addUpdate(part_idx, function Cluster.addElement(cref = name_cref, ty = ClusterElementType.VARIABLE), cluster_map); | |
| 1021 | end if; | ||
| 1022 | end for; | ||
| 1023 | |||
| 1024 |
2/2✓ Branch 1 taken 330 times.
✓ Branch 2 taken 190 times.
|
520 | for cluster in UnorderedMap.valueList(cluster_map) loop |
| 1025 | 330 | Cluster.addToClockMap(cluster, equations, info, clock_map); | |
| 1026 | end for; | ||
| 1027 | |||
| 1028 | // resolve inner sub clock dependencies | ||
| 1029 | 190 | ClockedInfo.resolveSubClocks(info, clock_map); | |
| 1030 | |||
| 1031 | // get the actual partitions from the clusters and split continuous/clocked | ||
| 1032 |
4/4✓ Branch 1 taken 330 times.
✓ Branch 2 taken 190 times.
✓ Branch 3 taken 330 times.
✓ Branch 4 taken 190 times.
|
520 | partitions := list(Cluster.toPartition(cl, variables, equations, kind, info, held_crefs, infer_del) for cl in UnorderedMap.valueList(cluster_map)); |
| 1033 | // update the partitions if one of their variables is in a hold() function | ||
| 1034 |
4/4✓ Branch 0 taken 330 times.
✓ Branch 1 taken 190 times.
✓ Branch 2 taken 330 times.
✓ Branch 3 taken 190 times.
|
520 | partitions := list(Partition.Partition.updateHeldVars(part, held_crefs) for part in partitions); |
| 1035 | // merge all clocked partitions with equal base and sub clock and find the proper order | ||
| 1036 |
5/6✗ Branch 2 not taken.
✓ Branch 3 taken 329 times.
✓ Branch 4 taken 329 times.
✓ Branch 5 taken 190 times.
✓ Branch 6 taken 329 times.
✓ Branch 7 taken 190 times.
|
519 | partitions := list(Partition.Partition.setIndex(partition, index) for partition guard(not Partition.Partition.isEmpty(partition)) in sortAndMergeClockedPartitions(partitions, info)); |
| 1037 | |||
| 1038 | // remove the unused inferred clocks from clock structure | ||
| 1039 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 190 times.
|
190 | for unused_infer in UnorderedSet.toList(infer_del) loop |
| 1040 | ✗ | UnorderedMap.remove(unused_infer, info.baseClocks); | |
| 1041 | ✗ | UnorderedMap.remove(unused_infer, info.baseToSub); | |
| 1042 | end for; | ||
| 1043 | |||
| 1044 | |||
| 1045 |
2/2✓ Branch 1 taken 189 times.
✓ Branch 2 taken 1 time.
|
190 | if Flags.isSet(Flags.DUMP_SYNCHRONOUS) then |
| 1046 | 1 | print(StringUtil.headline_1("[dumpSynchronous] Partitioning result:") + "\n" + List.toString(partitions, function Partition.Partition.toString(level = 2), List.Style.NEWLINE) + "\n"); | |
| 1047 | 1 | print(ClockedInfo.toString(info)); | |
| 1048 | end if; | ||
| 1049 | end partitioningClocked; | ||
| 1050 | |||
| 1051 | function sortAndMergeClockedPartitions | ||
| 1052 | input output list<Partition.Partition> partitions; | ||
| 1053 | input ClockedInfo info; | ||
| 1054 | protected | ||
| 1055 | list<Partition.Partition> clocked_partitions; | ||
| 1056 | list<list<Partition.Partition>> new_clocked = {}; | ||
| 1057 | // type for for double map. base clock -> {sub_clock -> partition} | ||
| 1058 | type SubMap = UnorderedMap<BClock, Partition.Partition>; | ||
| 1059 | UnorderedMap<BClock, SubMap> clock_collector = UnorderedMap.new<SubMap>(BClock.hash, BClock.isEqual); | ||
| 1060 | UnorderedMap<ComponentRef, BClock> base_clock_inferrence = UnorderedMap.new<BClock>(ComponentRef.hash, ComponentRef.isEqual); | ||
| 1061 | BClock clock, baseClock, subClock; | ||
| 1062 | Option<BClock> baseClock_opt; | ||
| 1063 | SubMap subClockMap "maps sub clock to it's partition for current base clock"; | ||
| 1064 | Partition.Partition new_part; | ||
| 1065 | algorithm | ||
| 1066 | // filter all clocked partitions | ||
| 1067 | 190 | (clocked_partitions, partitions) := List.splitOnTrue(partitions, Partition.Partition.isClocked); | |
| 1068 | |||
| 1069 | // initialize for all base clocks | ||
| 1070 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 190 times.
|
202 | for baseClock in UnorderedMap.valueList(info.baseClocks) loop |
| 1071 | 12 | UnorderedMap.add(baseClock, UnorderedMap.new<Partition.Partition>(BClock.hash, BClock.isEqual), clock_collector); | |
| 1072 | end for; | ||
| 1073 | |||
| 1074 | // collect the base clock inferrence data to correctly associate the inferred clocks | ||
| 1075 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 190 times.
|
209 | for partition in clocked_partitions loop |
| 1076 | 19 | (clock, baseClock_opt, _) := Partition.Partition.getClocks(partition); | |
| 1077 | clock := match baseClock_opt | ||
| 1078 | case SOME(clock) then clock; | ||
| 1079 | else clock; | ||
| 1080 | end match; | ||
| 1081 |
2/2✓ Branch 1 taken 24 times.
✓ Branch 2 taken 19 times.
|
43 | for var in VariablePointers.toList(partition.unknowns) loop |
| 1082 | 24 | UnorderedMap.add(BVariable.getVarName(var), clock, base_clock_inferrence); | |
| 1083 | end for; | ||
| 1084 | end for; | ||
| 1085 | |||
| 1086 | // merge clocked partitions by baseclock, subclock | ||
| 1087 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 190 times.
|
209 | for partition in clocked_partitions loop |
| 1088 | 19 | (clock, baseClock_opt, _) := Partition.Partition.getClocks(partition); | |
| 1089 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 15 times.
|
19 | if isSome(baseClock_opt) then |
| 1090 | // it is a sub clock | ||
| 1091 | 15 | SOME(baseClock) := baseClock_opt; | |
| 1092 | 15 | baseClock := BClock.baseClockInferrence(baseClock, base_clock_inferrence); | |
| 1093 | subClock := clock; | ||
| 1094 | else | ||
| 1095 | // it is a base clock, use the default sub clock | ||
| 1096 | 4 | baseClock := BClock.baseClockInferrence(clock, base_clock_inferrence); | |
| 1097 | subClock := DEFAULT_SUB_CLOCK; | ||
| 1098 | end if; | ||
| 1099 | 19 | partition := Partition.Partition.setClocks(partition, subClock, SOME(baseClock)); | |
| 1100 | 19 | subClockMap := UnorderedMap.getSafe(baseClock, clock_collector, sourceInfo()); | |
| 1101 | new_part := match UnorderedMap.get(subClock, subClockMap) | ||
| 1102 | 2 | case SOME(new_part) then Partition.Partition.merge(new_part, partition, true); | |
| 1103 | else partition; | ||
| 1104 | end match; | ||
| 1105 | 19 | UnorderedMap.add(subClock, new_part, subClockMap); | |
| 1106 | end for; | ||
| 1107 | |||
| 1108 | // recollect all clocked partitions and sort the sub partitions. resolve potential artifical algebraic loops | ||
| 1109 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 190 times.
|
202 | for tpl in UnorderedMap.toList(clock_collector) loop |
| 1110 | 12 | (baseClock, subClockMap) := tpl; | |
| 1111 | 12 | new_clocked := sortClockedPartitions(UnorderedMap.valueList(subClockMap)) :: new_clocked; | |
| 1112 | end for; | ||
| 1113 | |||
| 1114 | // append all clocked partitions in the end and apply proper indexing | ||
| 1115 |
4/4✓ Branch 2 taken 202 times.
✓ Branch 3 taken 190 times.
✓ Branch 4 taken 202 times.
✓ Branch 5 taken 190 times.
|
392 | partitions := listAppend(partition for partition in listReverse(partitions :: new_clocked)); |
| 1116 | end sortAndMergeClockedPartitions; | ||
| 1117 | |||
| 1118 | function sortClockedPartitions | ||
| 1119 | "use tarjan to sort sub partitions that rely on order. if an artificial algebraic loop occurs, | ||
| 1120 | break up the partitions such that the loop does not occur anymore. Actual algebraic loops with | ||
| 1121 | different clocks are forbidden by Section 16.7.4 in the Modelica Specification Version 3.7, | ||
| 1122 | therefore an issue will be raised in that case." | ||
| 1123 | input list<Partition.Partition> unsorted; | ||
| 1124 | output list<Partition.Partition> sorted = {}; | ||
| 1125 | protected | ||
| 1126 | Integer n = listLength(unsorted); | ||
| 1127 | array<Partition.Partition> partitions = listArray(listReverse(unsorted)); | ||
| 1128 | Adjacency.IntMatrix.Builder m = Adjacency.IntMatrix.newBuilder(n); | ||
| 1129 | // create a trivial matching for an artificially matched bipartite graph (tarjan implementation needs it) | ||
| 1130 | Matching matching = Matching.trivial(n); | ||
| 1131 | UnorderedMap<BClock, Integer> index_map = UnorderedMap.new<Integer>(BClock.hash, BClock.isEqual); | ||
| 1132 | list<list<Integer>> partition_order; | ||
| 1133 | Integer j; | ||
| 1134 | algorithm | ||
| 1135 | // prepare the clock to partition index map | ||
| 1136 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 time.
|
29 | for i in 1:n loop |
| 1137 | 17 | UnorderedMap.add(Partition.Partition.getClocks(partitions[i]), i, index_map); | |
| 1138 | end for; | ||
| 1139 | |||
| 1140 | // fill the adjacency matrix | ||
| 1141 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 time.
|
29 | for i in 1:n loop |
| 1142 |
2/2✓ Branch 3 taken 5 times.
✓ Branch 4 taken 17 times.
|
22 | for clock in UnorderedSet.toList(Partition.Partition.getClockDependencies(partitions[i])) loop |
| 1143 | 5 | j := UnorderedMap.getSafe(clock, index_map, sourceInfo()); | |
| 1144 | 5 | Adjacency.IntMatrix.builderAdd(m, i, j); | |
| 1145 | end for; | ||
| 1146 | end for; | ||
| 1147 | |||
| 1148 | // use tarjan to sort the artificial bipartite graph | ||
| 1149 | 12 | partition_order := Sorting.tarjanScalar(Adjacency.IntMatrix.fromBuilder(m, n), matching); | |
| 1150 | |||
| 1151 | // use the strong components to sort partitions. no algebraic loops allowed | ||
| 1152 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 12 times.
|
28 | for comp in listReverse(partition_order) loop |
| 1153 | sorted := match comp | ||
| 1154 | local | ||
| 1155 | UnorderedMap<VariablePointer, BClock> var_clock_map; | ||
| 1156 | Partition.Partition part; | ||
| 1157 | list<StrongComponent> sub_comps; | ||
| 1158 | list<Pointer<Variable>> sub_comp_vars; | ||
| 1159 | list<Pointer<Equation>> sub_comp_eqns; | ||
| 1160 | Option<tuple<list<Pointer<Variable>>, list<Pointer<Equation>>, BClock>> collector; | ||
| 1161 | UnorderedSet<BClock> var_clocks; | ||
| 1162 | Option<BClock> baseClock; | ||
| 1163 | |||
| 1164 | list<Pointer<Variable>> vars; | ||
| 1165 | list<Pointer<Equation>> eqns; | ||
| 1166 | BClock clock, new_clock; | ||
| 1167 | |||
| 1168 | // standard non loop partition | ||
| 1169 | 15 | case {j} then partitions[j] :: sorted; | |
| 1170 | |||
| 1171 | // multiple partitions form an artificial algebraic loop | ||
| 1172 | else algorithm | ||
| 1173 | // save which variable listens to which clock | ||
| 1174 | 1 | var_clock_map := UnorderedMap.new<BClock>(BVariable.hash, BVariable.equalName); | |
| 1175 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
|
3 | for i in comp loop |
| 1176 | 2 | part := partitions[i]; | |
| 1177 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
|
6 | for var in VariablePointers.toList(part.unknowns) loop |
| 1178 | 4 | UnorderedMap.add(var, Partition.Partition.getClocks(part), var_clock_map); | |
| 1179 | end for; | ||
| 1180 | end for; | ||
| 1181 | |||
| 1182 | // merge all partitions (allow different clocks) | ||
| 1183 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | j :: comp := comp; |
| 1184 | 1 | part := partitions[j]; | |
| 1185 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
|
2 | for i in comp loop |
| 1186 | 1 | part := Partition.Partition.merge(part, partitions[i], false); | |
| 1187 | end for; | ||
| 1188 | |||
| 1189 | // all base clocks have to be equal, just get any of them | ||
| 1190 | 1 | (_, baseClock, _) := Partition.Partition.getClocks(part); | |
| 1191 | |||
| 1192 | // causalize | ||
| 1193 | 1 | (_, sub_comps) := Causalize.simple(part.unknowns, part.equations, Partition.Partition.getKind(part)); | |
| 1194 | |||
| 1195 | // split the resulting strong components into partitions by clocks | ||
| 1196 | collector := NONE(); | ||
| 1197 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 time.
|
5 | for sub_comp in listReverse(sub_comps) loop |
| 1198 | 4 | sub_comp_vars := StrongComponent.getVariables(sub_comp); | |
| 1199 | 4 | sub_comp_eqns := StrongComponent.getEquations(sub_comp); | |
| 1200 | 4 | var_clocks := UnorderedSet.new(BClock.hash, BClock.isEqual); | |
| 1201 | |||
| 1202 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for var in sub_comp_vars loop |
| 1203 | 4 | UnorderedSet.add(UnorderedMap.getSafe(var, var_clock_map, sourceInfo()), var_clocks); | |
| 1204 | end for; | ||
| 1205 | |||
| 1206 | // update the collector, either 1) make new collector, 2) add to existing collector 3) finalize partition and make new collector | ||
| 1207 | collector := match (collector, UnorderedSet.toList(var_clocks)) | ||
| 1208 | // 1) no previous collector, make new one | ||
| 1209 | 1 | case (NONE(), {new_clock}) then SOME((sub_comp_vars, sub_comp_eqns, new_clock)); | |
| 1210 | |||
| 1211 | // previous collector, check if still same clock | ||
| 1212 | case (SOME((vars, eqns, clock)), {new_clock}) algorithm | ||
| 1213 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
|
3 | if BClock.isEqual(clock, new_clock) then |
| 1214 | // 2) same clock, just append to partition | ||
| 1215 | 1 | collector := SOME((listAppend(sub_comp_vars, vars), listAppend(sub_comp_eqns, eqns), clock)); | |
| 1216 | else | ||
| 1217 | // 3) new clock, split partition | ||
| 1218 | // ToDo: keep sorting and reuse later | ||
| 1219 | 2 | part := Partition.PARTITION(0, Partition.Association.CLOCKED(clock, baseClock, UnorderedSet.new(BClock.hash, BClock.isEqual), false), | |
| 1220 | VariablePointers.fromList(vars), NONE(), EquationPointers.fromList(eqns), NONE(), NONE(), NONE()); | ||
| 1221 | sorted := part :: sorted; | ||
| 1222 | |||
| 1223 | // open new collector | ||
| 1224 | 2 | collector := SOME((sub_comp_vars, sub_comp_eqns, new_clock)); | |
| 1225 | end if; | ||
| 1226 | then collector; | ||
| 1227 | |||
| 1228 | else algorithm | ||
| 1229 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for sub-partitions with cyclic dependency that could not be resolved:\n" | |
| 1230 | + "There are contradicting sub-clocks: " + List.toString(UnorderedSet.toList(var_clocks), BClock.toString) + " in strong component:\n" | ||
| 1231 | + StrongComponent.toString(sub_comp)}); | ||
| 1232 | ✗ | then fail(); | |
| 1233 | end match; | ||
| 1234 | end for; | ||
| 1235 | |||
| 1236 | // finalize last partition | ||
| 1237 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
|
1 | if isSome(collector) then |
| 1238 | 1 | SOME((vars, eqns, clock)) := collector; | |
| 1239 | 1 | part := Partition.PARTITION(0, Partition.Association.CLOCKED(clock, baseClock, UnorderedSet.new(BClock.hash, BClock.isEqual), false), | |
| 1240 | VariablePointers.fromList(vars), NONE(), EquationPointers.fromList(eqns), NONE(), NONE(), NONE()); | ||
| 1241 | sorted := part :: sorted; | ||
| 1242 | end if; | ||
| 1243 | then sorted; | ||
| 1244 | end match; | ||
| 1245 | end for; | ||
| 1246 | end sortClockedPartitions; | ||
| 1247 | |||
| 1248 | function collectPartitioningCrefs | ||
| 1249 | input output Expression exp; | ||
| 1250 | input UnorderedSet<ComponentRef> var_crefs; | ||
| 1251 | algorithm | ||
| 1252 | exp := match exp | ||
| 1253 | local | ||
| 1254 | Expression newExp; | ||
| 1255 | Call call; | ||
| 1256 | Expression arg; | ||
| 1257 | list<ComponentRef> children; | ||
| 1258 | ComponentRef stripped; | ||
| 1259 | |||
| 1260 | // clocked partitioning special rules | ||
| 1261 | case Expression.CALL(call = call as Call.TYPED_CALL()) algorithm | ||
| 1262 | newExp := match AbsynUtil.pathString(Function.nameConsiderBuiltin(call.fn)) | ||
| 1263 | // skip these as they do not cause dependency | ||
| 1264 | case "subSample" then exp; | ||
| 1265 | case "superSample" then exp; | ||
| 1266 | case "shiftSample" then exp; | ||
| 1267 | case "backSample" then exp; | ||
| 1268 | case "previous" then exp; | ||
| 1269 | case "hold" then exp; | ||
| 1270 | // sample can have dependencies | ||
| 1271 | case "sample" algorithm | ||
| 1272 | arg := match Call.arguments(exp.call) | ||
| 1273 | // not collected samples have 2 arguments | ||
| 1274 | case {_, arg} then arg; | ||
| 1275 | // collected samples have 3 arguments | ||
| 1276 | case {_, _, arg} then arg; | ||
| 1277 | else algorithm | ||
| 1278 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for: " + Expression.toString(exp) + "."}); | |
| 1279 | ✗ | then fail(); | |
| 1280 | end match; | ||
| 1281 | 29 | then Expression.mapShallow(arg, function collectPartitioningCrefs(var_crefs = var_crefs)); | |
| 1282 | 720 | else Expression.mapShallow(exp, function collectPartitioningCrefs(var_crefs = var_crefs)); | |
| 1283 | end match; | ||
| 1284 | then newExp; | ||
| 1285 | |||
| 1286 | // get all variable crefs for this cref and add to set | ||
| 1287 | case Expression.CREF() algorithm | ||
| 1288 | // extract potential record children | ||
| 1289 | children := match BVariable.getVar(exp.cref, sourceInfo()) | ||
| 1290 | local | ||
| 1291 | list<PointerWeak<Variable>> children_vars; | ||
| 1292 | case Variable.VARIABLE(backendinfo = BackendInfo.BACKEND_INFO(varKind = VariableKind.RECORD(children = children_vars))) | ||
| 1293 |
4/4✓ Branch 0 taken 75 times.
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 75 times.
✓ Branch 3 taken 31 times.
|
106 | then list(BVariable.getVarName(PointerWeak.upgrade(var)) for var in children_vars); |
| 1294 | 9680 | else {exp.cref}; | |
| 1295 | end match; | ||
| 1296 | |||
| 1297 |
2/2✓ Branch 0 taken 9755 times.
✓ Branch 1 taken 9711 times.
|
19466 | for child in children loop |
| 1298 | // check if cref has to be considered as a dependency | ||
| 1299 | 9755 | stripped := ComponentRef.stripSubscriptsAll(child); | |
| 1300 |
2/2✓ Branch 1 taken 8087 times.
✓ Branch 2 taken 1668 times.
|
9755 | if not BVariable.checkCref(stripped, BVariable.isParamOrConst, sourceInfo()) then |
| 1301 | 8087 | addCrefToSet(stripped, var_crefs); | |
| 1302 | end if; | ||
| 1303 | end for; | ||
| 1304 | then exp; | ||
| 1305 | |||
| 1306 | 7547 | else Expression.mapShallow(exp, function collectPartitioningCrefs(var_crefs = var_crefs)); | |
| 1307 | end match; | ||
| 1308 | end collectPartitioningCrefs; | ||
| 1309 | |||
| 1310 | function addCrefToSet | ||
| 1311 | input ComponentRef cref; | ||
| 1312 | input UnorderedSet<ComponentRef> set; | ||
| 1313 | protected | ||
| 1314 | Pointer<Variable> var_ptr = BVariable.getVarPointer(cref, sourceInfo()); | ||
| 1315 | algorithm | ||
| 1316 | // states and there derivatives belong to one partition | ||
| 1317 | // discrete states and there pre value also | ||
| 1318 |
2/2✓ Branch 1 taken 401 times.
✓ Branch 2 taken 7686 times.
|
8087 | if BVariable.isState(var_ptr) then |
| 1319 | 401 | UnorderedSet.add(BVariable.getPartnerCref(cref, BVariable.getVarDer), set); | |
| 1320 | elseif BVariable.isPrevious(var_ptr) then | ||
| 1321 | 74 | UnorderedSet.add(BVariable.getPartnerCref(cref, BVariable.getVarPre), set); | |
| 1322 | else | ||
| 1323 | 7612 | UnorderedSet.add(cref, set); | |
| 1324 | end if; | ||
| 1325 | end addCrefToSet; | ||
| 1326 | |||
| 1327 | function replaceClockedFunctions | ||
| 1328 | "replaces sample() and hold() calls using clocks as condition with the $getPart function" | ||
| 1329 | input output Expression exp; | ||
| 1330 | input UnorderedSet<ComponentRef> held_crefs; | ||
| 1331 | function replaceSample | ||
| 1332 | input output Expression exp; | ||
| 1333 | input Call call; | ||
| 1334 | input Boolean basic; | ||
| 1335 | protected | ||
| 1336 | Expression arg1, arg2; | ||
| 1337 | algorithm | ||
| 1338 | {arg1, arg2} := match Call.arguments(call) | ||
| 1339 | // not collected samples have 2 arguments | ||
| 1340 | case {arg1, arg2} then {arg1, arg2}; | ||
| 1341 | // collected samples have 3 arguments | ||
| 1342 | case {_, arg1, arg2} guard(basic) then {arg1, arg2}; | ||
| 1343 | // non basic with 3 arguments only care for the first argument as signal | ||
| 1344 | case {arg1, arg2, _} then {arg1, arg2}; | ||
| 1345 | else algorithm | ||
| 1346 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for: " + Expression.toString(exp) + "."}); | |
| 1347 | ✗ | then fail(); | |
| 1348 | end match; | ||
| 1349 | // if it's the basic sample operator, the second argument is supposed to be the clock, otherwise the first | ||
| 1350 |
2/2✓ Branch 0 taken 29 times.
✓ Branch 1 taken 6 times.
|
35 | if basic then |
| 1351 |
2/2✓ Branch 2 taken 7 times.
✓ Branch 3 taken 22 times.
|
29 | exp := if Type.isClock(Expression.typeOf(arg2)) then replaceClockedFunctionExp(arg1) else exp; |
| 1352 | else | ||
| 1353 | 6 | exp := replaceClockedFunctionExp(arg1); | |
| 1354 | end if; | ||
| 1355 | end replaceSample; | ||
| 1356 | algorithm | ||
| 1357 | exp := match exp | ||
| 1358 | local | ||
| 1359 | Expression newExp, arg; | ||
| 1360 | Call call; | ||
| 1361 | |||
| 1362 | case Expression.CALL(call = call as Call.TYPED_CALL()) algorithm | ||
| 1363 | newExp := match AbsynUtil.pathString(Function.nameConsiderBuiltin(call.fn)) | ||
| 1364 | // sample cases | ||
| 1365 | 29 | case "sample" then replaceSample(exp, call, true); | |
| 1366 | 4 | case "subSample" then replaceSample(exp, call, false); | |
| 1367 | 1 | case "superSample" then replaceSample(exp, call, false); | |
| 1368 | ✗ | case "shiftSample" then replaceSample(exp, call, false); | |
| 1369 | 1 | case "backSample" then replaceSample(exp, call, false); | |
| 1370 | |||
| 1371 | // hold case | ||
| 1372 | case "hold" algorithm | ||
| 1373 | arg := match Call.arguments(exp.call) | ||
| 1374 | // hold can only have one argument | ||
| 1375 | case {arg as Expression.CREF()} algorithm | ||
| 1376 | ✗ | UnorderedSet.add(arg.cref, held_crefs); | |
| 1377 | then arg; | ||
| 1378 | else algorithm | ||
| 1379 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for: " + Expression.toString(exp) + "."}); | |
| 1380 | ✗ | then fail(); | |
| 1381 | end match; | ||
| 1382 | ✗ | then replaceClockedFunctionExp(arg); | |
| 1383 | |||
| 1384 | else exp; | ||
| 1385 | end match; | ||
| 1386 | then newExp; | ||
| 1387 | else exp; | ||
| 1388 | end match; | ||
| 1389 | end replaceClockedFunctions; | ||
| 1390 | |||
| 1391 | function replaceClockedFunctionExp | ||
| 1392 | input output Expression exp; | ||
| 1393 | protected | ||
| 1394 | Function func; | ||
| 1395 | algorithm | ||
| 1396 | func := match Expression.typeOf(exp) | ||
| 1397 | 13 | case Type.REAL() then NFBuiltinFuncs.GET_PART_REAL; | |
| 1398 | ✗ | case Type.INTEGER() then NFBuiltinFuncs.GET_PART_INT; | |
| 1399 | ✗ | case Type.BOOLEAN() then NFBuiltinFuncs.GET_PART_BOOL; | |
| 1400 | ✗ | case Type.CLOCK() then NFBuiltinFuncs.GET_PART_CLOCK; | |
| 1401 | else algorithm | ||
| 1402 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed. " + Expression.toString(exp) + " is of type " | |
| 1403 | + Type.toString(Expression.typeOf(exp)) + ", only real, integer, boolean and clock are allowed."}); | ||
| 1404 | ✗ | then fail(); | |
| 1405 | end match; | ||
| 1406 | 13 | exp := Expression.CALL(Call.makeTypedCall( | |
| 1407 | fn = func, | ||
| 1408 | args = {exp}, | ||
| 1409 | variability = Expression.variability(exp), | ||
| 1410 | purity = NFPrefixes.Purity.PURE | ||
| 1411 | )); | ||
| 1412 | end replaceClockedFunctionExp; | ||
| 1413 | |||
| 1414 | function replaceClockedWhen | ||
| 1415 | "replace clocked when equations in clocked partitions with their body statement. | ||
| 1416 | only works for split up when equations with a single statement and no else when." | ||
| 1417 | input output Equation eqn; | ||
| 1418 | algorithm | ||
| 1419 | eqn := match eqn | ||
| 1420 | local | ||
| 1421 | Expression cond; | ||
| 1422 | WhenStatement stmt; | ||
| 1423 | |||
| 1424 | case Equation.WHEN_EQUATION(body = WhenEquationBody.WHEN_EQUATION_BODY(condition = cond, when_stmts = {stmt}, else_when = NONE())) | ||
| 1425 | guard(Type.isClock(Expression.typeOf(cond))) | ||
| 1426 | 8 | then WhenStatement.toEquation(stmt, eqn.attr, false); | |
| 1427 | |||
| 1428 | else eqn; | ||
| 1429 | end match; | ||
| 1430 | end replaceClockedWhen; | ||
| 1431 | |||
| 1432 | annotation(__OpenModelica_Interface="nbackend"); | ||
| 1433 | end NBPartitioning; | ||
| 1434 |