OMCompiler/Compiler/NBackEnd/Modules/2_Pre/NBEvents.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 NBEvents | ||
| 37 | "file: NBEvents.mo | ||
| 38 | package: NBEvents | ||
| 39 | description: This file contains the functions for the event collection module. | ||
| 40 | " | ||
| 41 | |||
| 42 | public | ||
| 43 | import Module = NBModule; | ||
| 44 | |||
| 45 | protected | ||
| 46 | // OF imports | ||
| 47 | import Absyn.Path; | ||
| 48 | |||
| 49 | // NF | ||
| 50 | import Algorithm = NFAlgorithm; | ||
| 51 | import Builtin = NFBuiltin; | ||
| 52 | import Call = NFCall; | ||
| 53 | import ClockKind = NFClockKind; | ||
| 54 | import ComponentRef = NFComponentRef; | ||
| 55 | import Expression = NFExpression; | ||
| 56 | import ExpandExp = NFExpandExp; | ||
| 57 | import NFFunction.Function; | ||
| 58 | import Operator = NFOperator; | ||
| 59 | import Prefixes = NFPrefixes; | ||
| 60 | import SimplifyExp = NFSimplifyExp; | ||
| 61 | import Statement = NFStatement; | ||
| 62 | import Subscript = NFSubscript; | ||
| 63 | import Type = NFType; | ||
| 64 | import Variable = NFVariable; | ||
| 65 | |||
| 66 | // OB | ||
| 67 | import OldBackendDAE = BackendDAE; | ||
| 68 | import OldExpression = Expression; | ||
| 69 | |||
| 70 | // New Backend | ||
| 71 | import BackendDAE = NBackendDAE; | ||
| 72 | import BEquation = NBEquation; | ||
| 73 | import NBEquation.{Equation, Frame, Iterator, EqData, EquationAttributes, EquationKind, EquationPointers, IfEquationBody, WhenEquationBody}; | ||
| 74 | import Solve = NBSolve; | ||
| 75 | import BVariable = NBVariable; | ||
| 76 | import NBVariable.{VarData, VariablePointers}; | ||
| 77 | |||
| 78 | // SimCode | ||
| 79 | import NSimGenericCall.SimIterator; | ||
| 80 | import OldSimIterator = BackendDAE.SimIterator; | ||
| 81 | import Block = NSimStrongComponent.Block; | ||
| 82 | |||
| 83 | // Old Simcode | ||
| 84 | import OldSimCode = SimCode; | ||
| 85 | |||
| 86 | // Util | ||
| 87 | import BackendUtil = NBBackendUtil; | ||
| 88 | import Config; | ||
| 89 | import StringUtil; | ||
| 90 | |||
| 91 | // ========================================================================= | ||
| 92 | // MAIN ROUTINE, PLEASE DO NOT CHANGE | ||
| 93 | // ========================================================================= | ||
| 94 | public | ||
| 95 | function main | ||
| 96 | "Wrapper function for any event collection function. This will be | ||
| 97 | called during simulation and gets the corresponding subfunction from | ||
| 98 | Config." | ||
| 99 | extends Module.wrapper; | ||
| 100 | protected | ||
| 101 | Module.eventsInterface func; | ||
| 102 | algorithm | ||
| 103 | 190 | func := getModule(); | |
| 104 | |||
| 105 | bdae := match bdae | ||
| 106 | local | ||
| 107 | VarData varData; | ||
| 108 | EqData eqData; | ||
| 109 | EventInfo eventInfo; | ||
| 110 | |||
| 111 | case BackendDAE.MAIN() | ||
| 112 | algorithm | ||
| 113 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 190 times.
|
190 | (varData, eqData, eventInfo) := func(bdae.varData, bdae.eqData, bdae.eventInfo, bdae.funcMap); |
| 114 | 190 | bdae.varData := varData; | |
| 115 | 190 | bdae.eqData := eqData; | |
| 116 | 190 | bdae.eventInfo := eventInfo; | |
| 117 | then bdae; | ||
| 118 | |||
| 119 | else algorithm | ||
| 120 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."}); | |
| 121 | ✗ | then fail(); | |
| 122 | end match; | ||
| 123 | end main; | ||
| 124 | |||
| 125 | function getModule | ||
| 126 | "Returns the module function that was chosen by the user." | ||
| 127 | output Module.eventsInterface func; | ||
| 128 | protected | ||
| 129 | String flag = "default"; //Flags.getConfigString(Flags.ZERO_CROSSINGS) | ||
| 130 | algorithm | ||
| 131 | func := match flag | ||
| 132 | case "default" then (eventsDefault); | ||
| 133 | /* ... New detect states modules have to be added here */ | ||
| 134 | else fail(); | ||
| 135 | end match; | ||
| 136 | end getModule; | ||
| 137 | |||
| 138 | // ========================================================================= | ||
| 139 | // TYPES, UNIONTYPES AND MEMBER FUNCTIONS | ||
| 140 | // ========================================================================= | ||
| 141 | |||
| 142 | uniontype EventInfo | ||
| 143 | record EVENT_INFO | ||
| 144 | UnorderedSet<TimeEvent> time_set "tracks compact time events (SINGLE or SAMPLE)"; | ||
| 145 | UnorderedMap<Condition, CompositeEvent> time_map "tracks full time events of the form $TEV_11 = ..."; | ||
| 146 | UnorderedMap<Condition, StateEvent> state_map "tracks full state events of the form $SEV_4 = ..."; | ||
| 147 | Integer numberMathEvents "stores the number of math function that trigger events e.g. floor, ceil, integer, ..."; | ||
| 148 | list<MathEvent> math_lst "math functions that trigger events, ordered by index"; | ||
| 149 | list<SpatialDistribution> spatial_lst "stores all spatial distribution calls"; | ||
| 150 | end EVENT_INFO; | ||
| 151 | |||
| 152 | function toString | ||
| 153 | input EventInfo eventInfo; | ||
| 154 | output String str = ""; | ||
| 155 | protected | ||
| 156 | list<TimeEvent> tev_lst; | ||
| 157 | list<tuple<Condition, CompositeEvent>> cev_lst; | ||
| 158 | list<tuple<Condition, StateEvent>> sev_lst; | ||
| 159 | function tplString<T1, T2> | ||
| 160 | input tuple<T1, T2> tpl; | ||
| 161 | input F1 f1; | ||
| 162 | input F2 f2; | ||
| 163 | output String str; | ||
| 164 | protected | ||
| 165 | T1 t1; | ||
| 166 | T2 t2; | ||
| 167 | partial function F1 input T1 t1; output String str; end F1; | ||
| 168 | partial function F2 input T2 t2; output String str; end F2; | ||
| 169 | algorithm | ||
| 170 | ✗ | (t1, t2) := tpl; | |
| 171 | ✗ | str := f2(t2) + " = " + f1(t1); | |
| 172 | end tplString; | ||
| 173 | algorithm | ||
| 174 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
15 | if not isEmpty(eventInfo) then |
| 175 | ✗ | (tev_lst, cev_lst, sev_lst) := toLists(eventInfo); | |
| 176 | ✗ | str := StringUtil.headline_2("Event Info") + "\n"; | |
| 177 | ✗ | str := str + StringUtil.headline_4("Time Events") + List.toString(tev_lst, function TimeEvent.toString(printIndex = true), List.Style.NEWLINE) + "\n\n"; | |
| 178 | ✗ | str := str + StringUtil.headline_4("Composite Events") + List.toString(cev_lst, function tplString(f1 = Condition.toString, f2 = CompositeEvent.toString), List.Style.NEWLINE) + "\n\n"; | |
| 179 | ✗ | str := str + StringUtil.headline_4("State Events") + List.toString(sev_lst, function tplString(f1 = Condition.toString, f2 = StateEvent.toString), List.Style.NEWLINE) + "\n\n"; | |
| 180 | end if; | ||
| 181 | end toString; | ||
| 182 | |||
| 183 | function toLists | ||
| 184 | input EventInfo eventInfo; | ||
| 185 | output list<TimeEvent> tev_lst; | ||
| 186 | output list<tuple<Condition, CompositeEvent>> cev_lst; | ||
| 187 | output list<tuple<Condition, StateEvent>> sev_lst; | ||
| 188 | algorithm | ||
| 189 | 188 | tev_lst := List.sort(UnorderedSet.toList(eventInfo.time_set), TimeEvent.indexGt); | |
| 190 | 188 | cev_lst := List.sort(UnorderedMap.toList(eventInfo.time_map), CompositeEvent.indexGt); | |
| 191 | 188 | sev_lst := List.sort(UnorderedMap.toList(eventInfo.state_map), StateEvent.indexGt); | |
| 192 | end toLists; | ||
| 193 | |||
| 194 | function create | ||
| 195 | input Bucket bucket; | ||
| 196 | input VariablePointers variables; | ||
| 197 | input Pointer<Integer> idx; | ||
| 198 | input list<SpatialDistribution> spatial_lst; | ||
| 199 | output EventInfo eventInfo; | ||
| 200 | output list<Pointer<Variable>> auxiliary_vars = {}; | ||
| 201 | output list<Pointer<Equation>> auxiliary_eqns = {}; | ||
| 202 | protected | ||
| 203 | Condition cond; | ||
| 204 | CompositeEvent cev; | ||
| 205 | StateEvent sev; | ||
| 206 | algorithm | ||
| 207 | // get auxiliary eqns and vars from composite events | ||
| 208 |
2/2✓ Branch 1 taken 34 times.
✓ Branch 2 taken 190 times.
|
224 | for tpl in UnorderedMap.toList(bucket.time_map) loop |
| 209 | 34 | (cond, cev) := tpl; | |
| 210 | 34 | (auxiliary_vars, auxiliary_eqns) := createAux(cond, cev.auxiliary, variables, idx, auxiliary_vars, auxiliary_eqns); | |
| 211 | end for; | ||
| 212 | |||
| 213 | // get auxiliary eqns and vars from state events | ||
| 214 |
2/2✓ Branch 1 taken 82 times.
✓ Branch 2 taken 190 times.
|
272 | for tpl in UnorderedMap.toList(bucket.state_map) loop |
| 215 | 82 | (cond, sev) := tpl; | |
| 216 | 82 | (auxiliary_vars, auxiliary_eqns) := createAux(cond, sev.auxiliary, variables, idx, auxiliary_vars, auxiliary_eqns); | |
| 217 | end for; | ||
| 218 | |||
| 219 | 190 | eventInfo := EVENT_INFO( | |
| 220 | time_set = bucket.time_set, | ||
| 221 | time_map = bucket.time_map, | ||
| 222 | state_map = bucket.state_map, // ToDo: StateEvent.updateIndices(stateEvents), | ||
| 223 | numberMathEvents = bucket.math_index, | ||
| 224 | math_lst = List.sort(UnorderedMap.valueList(bucket.math_map), MathEvent.indexGt), | ||
| 225 | spatial_lst = spatial_lst | ||
| 226 | ); | ||
| 227 | |||
| 228 |
1/2✓ Branch 1 taken 190 times.
✗ Branch 2 not taken.
|
190 | if Flags.isSet(Flags.DUMP_EVENTS) then |
| 229 | ✗ | print(toString(eventInfo)); | |
| 230 | ✗ | print(List.toStringCustom(auxiliary_eqns, function Equation.pointerToString(str = " "), StringUtil.headline_4("Event Equations"), "", "\n", "\n\n")); | |
| 231 | end if; | ||
| 232 | end create; | ||
| 233 | |||
| 234 | function createAux | ||
| 235 | input Condition cond; | ||
| 236 | input Pointer<Variable> aux_var; | ||
| 237 | input VariablePointers variables; | ||
| 238 | input Pointer<Integer> idx; | ||
| 239 | input output list<Pointer<Variable>> auxiliary_vars; | ||
| 240 | input output list<Pointer<Equation>> auxiliary_eqns; | ||
| 241 | protected | ||
| 242 | ComponentRef lhs_cref; | ||
| 243 | Pointer<Equation> aux_eqn; | ||
| 244 | algorithm | ||
| 245 | // if it has a statement index, it already has been created as a statement inside an algorithm (0 implies no index) | ||
| 246 |
2/2✓ Branch 0 taken 99 times.
✓ Branch 1 taken 17 times.
|
116 | if cond.stmt_index == 0 then |
| 247 | // lower the subscripts (containing iterators) | ||
| 248 | 99 | lhs_cref := ComponentRef.mapSubscripts(BVariable.getVarName(aux_var), function Subscript.mapExp( | |
| 249 | func = function BackendDAE.lowerComponentReferenceExp(variables = variables, complete = true))); | ||
| 250 | 99 | aux_eqn := Equation.makeAssignment(Expression.fromCref(lhs_cref), cond.exp, idx, "EVT", cond.iter, EquationAttributes.default(EquationKind.DISCRETE, false)); | |
| 251 | auxiliary_eqns := aux_eqn :: auxiliary_eqns; | ||
| 252 | end if; | ||
| 253 | // remove all subscripts from the variable name | ||
| 254 | 116 | BVariable.setVarName(aux_var, ComponentRef.stripSubscriptsAll(BVariable.getVarName(aux_var))); | |
| 255 | auxiliary_vars := aux_var :: auxiliary_vars; | ||
| 256 | end createAux; | ||
| 257 | |||
| 258 | function createAuxStatements | ||
| 259 | input output list<Statement> new_stmts; | ||
| 260 | input Pointer<Bucket> bucket_ptr; | ||
| 261 | input VariablePointers variables; | ||
| 262 | protected | ||
| 263 | Bucket bucket = Pointer.access(bucket_ptr); | ||
| 264 | Statement new_stmt; | ||
| 265 | Condition cond; | ||
| 266 | ComponentRef aux; | ||
| 267 | algorithm | ||
| 268 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 281 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 270 times.
|
281 | if isSome(bucket.aux_stmts) then |
| 269 | // add all new statements to the algorithm body | ||
| 270 |
2/2✓ Branch 1 taken 17 times.
✓ Branch 2 taken 11 times.
|
28 | for tpl in Util.getOption(bucket.aux_stmts) loop |
| 271 | 17 | (cond, aux) := tpl; | |
| 272 | 17 | aux := ComponentRef.mapSubscripts(aux, function Subscript.mapExp(func = | |
| 273 | function BackendDAE.lowerComponentReferenceExp(variables = variables, complete = true))); | ||
| 274 | 17 | new_stmt := Statement.makeAssignment(Expression.fromCref(aux), cond.exp, ComponentRef.getSubscriptedType(aux), DAE.emptyElementSource); | |
| 275 | new_stmts := new_stmt :: new_stmts; | ||
| 276 | end for; | ||
| 277 | // remove the current statements because they have been added | ||
| 278 | 11 | bucket.aux_stmts := NONE(); | |
| 279 | 11 | Pointer.update(bucket_ptr, bucket); | |
| 280 | end if; | ||
| 281 | end createAuxStatements; | ||
| 282 | |||
| 283 | function empty | ||
| 284 | output EventInfo eventInfo; | ||
| 285 | algorithm | ||
| 286 | 193 | eventInfo := EVENT_INFO( | |
| 287 | time_set = UnorderedSet.new(TimeEvent.hash, TimeEvent.isEqual), | ||
| 288 | time_map = UnorderedMap.new<CompositeEvent>(Condition.hash, Condition.isEqual), | ||
| 289 | state_map = UnorderedMap.new<StateEvent>(Condition.hash, Condition.isEqual), | ||
| 290 | numberMathEvents = 0, | ||
| 291 | math_lst = {}, | ||
| 292 | spatial_lst = {} | ||
| 293 | ); | ||
| 294 | end empty; | ||
| 295 | |||
| 296 | function isEmpty | ||
| 297 | input EventInfo eventInfo; | ||
| 298 | output Boolean b; | ||
| 299 | algorithm | ||
| 300 |
4/8✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 15 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 15 times.
|
15 | b := UnorderedSet.isEmpty(eventInfo.time_set) and UnorderedMap.isEmpty(eventInfo.time_map) and UnorderedMap.isEmpty(eventInfo.state_map) and eventInfo.numberMathEvents == 0; |
| 301 | end isEmpty; | ||
| 302 | |||
| 303 | function convert | ||
| 304 | input EventInfo eventInfo; | ||
| 305 | output list<OldBackendDAE.ZeroCrossing> zeroCrossings; | ||
| 306 | output list<OldBackendDAE.ZeroCrossing> relations "the relations of the zero crossings, e.g. both of (a > b or c > d)"; | ||
| 307 | output list<OldBackendDAE.TimeEvent> timeEvents; | ||
| 308 | output OldSimCode.SpatialDistributionInfo spatialInfo; | ||
| 309 | input UnorderedMap<ComponentRef, Block> equation_map; | ||
| 310 | protected | ||
| 311 | list<TimeEvent> tev_lst; | ||
| 312 | list<tuple<Condition, CompositeEvent>> cev_lst; | ||
| 313 | list<tuple<Condition, StateEvent>> sev_lst; | ||
| 314 | Integer math_base; | ||
| 315 | algorithm | ||
| 316 | // add composite at some point? | ||
| 317 | 188 | (tev_lst, cev_lst, sev_lst) := toLists(eventInfo); | |
| 318 | // the zero crossings of math events are numbered after the ones of state events | ||
| 319 |
4/4✓ Branch 0 taken 82 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 82 times.
✓ Branch 3 taken 188 times.
|
270 | math_base := sum(Condition.numRelations(Util.tuple21(sev_tpl)) for sev_tpl in sev_lst); |
| 320 |
11/12✓ Branch 0 taken 82 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 82 times.
✓ Branch 3 taken 188 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 188 times.
✓ Branch 9 taken 5 times.
✓ Branch 10 taken 188 times.
✓ Branch 11 taken 5 times.
✓ Branch 12 taken 188 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 188 times.
|
275 | zeroCrossings := listAppend(list(StateEvent.convert(sev_tpl, equation_map) for sev_tpl in sev_lst), |
| 321 | list(MathEvent.convert(mev, math_base + i, equation_map) threaded for mev in eventInfo.math_lst, i in List.intRange(listLength(eventInfo.math_lst)))); | ||
| 322 |
4/4✓ Branch 0 taken 82 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 82 times.
✓ Branch 3 taken 188 times.
|
270 | relations := List.flatten(list(StateEvent.convertRelations(sev_tpl, equation_map) for sev_tpl in sev_lst)); |
| 323 |
4/4✓ Branch 0 taken 25 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 188 times.
|
213 | timeEvents := list(TimeEvent.convert(tev) for tev in tev_lst); |
| 324 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
|
188 | if listEmpty(eventInfo.spatial_lst) then |
| 325 | spatialInfo := OldSimCode.SPATIAL_DISTRIBUTION_INFO({}, 0); | ||
| 326 | else | ||
| 327 | ✗ | spatialInfo := OldSimCode.SPATIAL_DISTRIBUTION_INFO(list(SpatialDistribution.convert(sd) for sd in eventInfo.spatial_lst), listLength(eventInfo.spatial_lst) - 1); | |
| 328 | end if; | ||
| 329 | end convert; | ||
| 330 | end EventInfo; | ||
| 331 | |||
| 332 | uniontype TimeEvent | ||
| 333 | record SINGLE "e.g. time > 0.5" | ||
| 334 | Integer index "unique sample index"; | ||
| 335 | Expression trigger "single point in time that triggers it"; | ||
| 336 | Iterator iter "potential iterator"; | ||
| 337 | end SINGLE; | ||
| 338 | |||
| 339 | record SAMPLE "e.g. sample(1, 1)" | ||
| 340 | Integer index "unique sample index"; | ||
| 341 | Expression start "first trigger point"; | ||
| 342 | Expression interval "equidistant intervals"; | ||
| 343 | Iterator iter "potential iterator"; | ||
| 344 | end SAMPLE; | ||
| 345 | |||
| 346 | function toString | ||
| 347 | input TimeEvent timeEvent; | ||
| 348 | input Boolean printIndex = true "for hashing we want to supress index"; | ||
| 349 | output String str; | ||
| 350 | protected | ||
| 351 | Iterator iter; | ||
| 352 | algorithm | ||
| 353 | (str, iter) := match timeEvent | ||
| 354 | 15 | case SINGLE() then ("time > " + Expression.toString(timeEvent.trigger), timeEvent.iter); | |
| 355 | 40 | case SAMPLE() then ("sample(" + intString(timeEvent.index) + ", " + Expression.toString(timeEvent.start) + ", " + Expression.toString(timeEvent.interval) + ")", timeEvent.iter); | |
| 356 | else algorithm | ||
| 357 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."}); | |
| 358 | ✗ | then fail(); | |
| 359 | end match; | ||
| 360 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
|
55 | if not Iterator.isEmpty(iter) then |
| 361 | ✗ | str := str + " for {" + Iterator.toString(iter) + "}"; | |
| 362 | end if; | ||
| 363 |
1/2✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
|
55 | if printIndex then |
| 364 | ✗ | str := "(" + intString(getIndex(timeEvent)) + ") " + str; | |
| 365 | end if; | ||
| 366 | end toString; | ||
| 367 | |||
| 368 | function toStringList | ||
| 369 | input list<TimeEvent> events_lst; | ||
| 370 | output String str; | ||
| 371 | algorithm | ||
| 372 | ✗ | str := StringUtil.headline_4("Time Events"); | |
| 373 | ✗ | if listEmpty(events_lst) then | |
| 374 | ✗ | str := str + "\t<No Time Events>\n"; | |
| 375 | else | ||
| 376 | ✗ | str := str + stringDelimitList(list(toString(te) for te in events_lst), "\n"); | |
| 377 | end if; | ||
| 378 | end toStringList; | ||
| 379 | |||
| 380 | function hash | ||
| 381 | input TimeEvent tev; | ||
| 382 | output Integer h = stringHashDjb2(toString(tev, false)); | ||
| 383 | end hash; | ||
| 384 | |||
| 385 | function isEqual | ||
| 386 | input TimeEvent tev1; | ||
| 387 | input TimeEvent tev2; | ||
| 388 | output Boolean b; | ||
| 389 | algorithm | ||
| 390 | b := match (tev1, tev2) | ||
| 391 | 3 | case (SINGLE(), SINGLE()) then Expression.isEqual(tev1.trigger, tev2.trigger); | |
| 392 |
3/4✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
|
8 | case (SAMPLE(), SAMPLE()) then Expression.isEqual(tev1.start, tev2.start) and Expression.isEqual(tev1.interval, tev2.interval); |
| 393 | else false; | ||
| 394 | end match; | ||
| 395 | end isEqual; | ||
| 396 | |||
| 397 | function indexGt | ||
| 398 | input TimeEvent tev1; | ||
| 399 | input TimeEvent tev2; | ||
| 400 | output Boolean b = getIndex(tev1) > getIndex(tev2); | ||
| 401 | end indexGt; | ||
| 402 | |||
| 403 | function create | ||
| 404 | input output Expression exp; | ||
| 405 | input output Bucket bucket; | ||
| 406 | input Iterator iter; | ||
| 407 | input Pointer<Equation> eqn; | ||
| 408 | input UnorderedMap<Path, Function> funcMap; | ||
| 409 | input Boolean createEqn; | ||
| 410 | output Boolean failed = false "returns true if time event list could not be created"; | ||
| 411 | algorithm | ||
| 412 | (exp, bucket, failed) := match exp | ||
| 413 | local | ||
| 414 | Expression exp1, exp2; | ||
| 415 | Boolean b1, b2; | ||
| 416 | |||
| 417 | case Expression.LBINARY() | ||
| 418 | guard(Operator.getMathClassification(exp.operator) == NFOperator.MathClassification.LOGICAL) | ||
| 419 | algorithm | ||
| 420 | ✗ | (exp1, bucket, b1) := create(exp.exp1, bucket, iter, eqn, funcMap, createEqn); | |
| 421 | ✗ | (exp2, bucket, b2) := create(exp.exp2, bucket, iter, eqn, funcMap, createEqn); | |
| 422 | ✗ | failed := (b1 or b2); | |
| 423 | if not failed then | ||
| 424 | // we could simplify here | ||
| 425 | ✗ | exp.exp1 := exp1; | |
| 426 | ✗ | exp.exp2 := exp2; | |
| 427 | end if; | ||
| 428 | ✗ | then (exp, bucket, failed); | |
| 429 | |||
| 430 | 57 | else createSingleOrSample(exp, bucket, iter, eqn, funcMap); | |
| 431 | end match; | ||
| 432 | |||
| 433 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 31 times.
|
57 | if not failed then |
| 434 | 31 | (exp, bucket) := CompositeEvent.add(exp, iter, bucket, createEqn); | |
| 435 | end if; | ||
| 436 | end create; | ||
| 437 | |||
| 438 | function createSingleOrSample | ||
| 439 | "The cases: | ||
| 440 | 1. creates a single time event from a comparing binary expression which | ||
| 441 | has to only depend on time | ||
| 442 | 2. creates a sample time event from a sample operator | ||
| 443 | 3. fails for anything else | ||
| 444 | NOTE: create sample from sin and cos functions?" | ||
| 445 | input output Expression exp "has to be LBINARY() with comparing operator or a sample CALL()"; | ||
| 446 | input output Bucket bucket "bucket containing the events"; | ||
| 447 | input Iterator iter; | ||
| 448 | input Pointer<Equation> eqn; | ||
| 449 | input UnorderedMap<Path, Function> funcMap "function map for differentiation (solve)"; | ||
| 450 | output Boolean failed "true if it did not work to create a compact time event"; | ||
| 451 | algorithm | ||
| 452 | (exp, failed) := match exp | ||
| 453 | local | ||
| 454 | Equation tmpEqn; | ||
| 455 | Solve.Status status; | ||
| 456 | Boolean can_trigger; | ||
| 457 | Solve.RelationInversion invert; | ||
| 458 | Call call; | ||
| 459 | Expression trigger, new_exp; | ||
| 460 | TimeEvent timeEvent; | ||
| 461 | Operator time_op; | ||
| 462 | Pointer<Boolean> containsTime = Pointer.create(false); | ||
| 463 | |||
| 464 | // check for "sample" call | ||
| 465 | case Expression.CALL() algorithm | ||
| 466 | 22 | (call, bucket, failed, _) := createSample(exp.call, bucket, iter); | |
| 467 | 22 | exp.call := call; | |
| 468 | 22 | then (exp, failed); | |
| 469 | |||
| 470 | // try to extract single time event | ||
| 471 | case Expression.RELATION() | ||
| 472 | guard(Operator.getMathClassification(exp.operator) == NFOperator.MathClassification.RELATION) | ||
| 473 | algorithm | ||
| 474 | // create auxiliary equation and solve for TIME | ||
| 475 | 35 | tmpEqn := Pointer.access(Equation.makeAssignment(exp.exp1, exp.exp2, Pointer.create(0), NBVariable.TEMPORARY_STR, Iterator.EMPTY(), EquationAttributes.default(EquationKind.UNKNOWN, false))); | |
| 476 | 70 | Equation.map(tmpEqn, function containsTimeTraverseExp(b = containsTime), SOME(function containsTimeTraverseCref(b = containsTime))); | |
| 477 |
2/2✓ Branch 1 taken 33 times.
✓ Branch 2 taken 2 times.
|
35 | if Pointer.access(containsTime) then |
| 478 | 99 | (tmpEqn, status, invert) := Solve.solveBody(tmpEqn, NFBuiltin.TIME_CREF, funcMap); | |
| 479 |
2/4✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
|
33 | if status == NBSolve.Status.EXPLICIT and invert <> NBSolve.RelationInversion.UNKNOWN then |
| 480 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 33 times.
|
33 | SOME(trigger) := Equation.getRHS(tmpEqn); |
| 481 | // only cases for RelationInversion == TRUE or FALSE can be present | ||
| 482 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
|
33 | time_op := if invert == NBSolve.RelationInversion.TRUE then Operator.invert(exp.operator) else exp.operator; |
| 483 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 30 times.
|
33 | if Equation.isWhenEquation(eqn) then |
| 484 | // if it is a when equation check if it can even trigger | ||
| 485 | can_trigger := match time_op.op | ||
| 486 | case NFOperator.Op.GREATER then true; | ||
| 487 | case NFOperator.Op.GREATEREQ then true; | ||
| 488 | else false; | ||
| 489 | end match; | ||
| 490 | // if it can trigger replace it by the sample call, otherwise just make the trigger false | ||
| 491 | // an equal time event that already exists has to be reused with its index | ||
| 492 | if can_trigger then | ||
| 493 | 3 | timeEvent := getOrAdd(SINGLE(UnorderedSet.size(bucket.time_set), trigger, iter), bucket.time_set); | |
| 494 | 6 | new_exp := Expression.CALL(Call.makeTypedCall( | |
| 495 | fn = NFBuiltinFuncs.SAMPLE, | ||
| 496 | args = {Expression.INTEGER(getIndex(timeEvent) + 1), trigger, Expression.makeMaxValue(Type.REAL())}, | ||
| 497 | variability = NFPrefixes.Variability.DISCRETE, | ||
| 498 | purity = NFPrefixes.Purity.PURE | ||
| 499 | )); | ||
| 500 | else | ||
| 501 | new_exp := Expression.BOOLEAN(false); | ||
| 502 | end if; | ||
| 503 | 3 | failed := false; | |
| 504 | elseif Equation.isAlgorithm(eqn) then | ||
| 505 | // algorithms keep the relation (e.g. conditions of when statements) | ||
| 506 | 6 | timeEvent := getOrAdd(SINGLE(UnorderedSet.size(bucket.time_set), trigger, iter), bucket.time_set); | |
| 507 | 6 | failed := false; | |
| 508 | new_exp := exp; | ||
| 509 | else | ||
| 510 | // outside of when equations the relation has to keep its value between events, which only | ||
| 511 | // the relations of state events do. A plain time comparison would change during integration. | ||
| 512 | 24 | failed := true; | |
| 513 | new_exp := exp; | ||
| 514 | end if; | ||
| 515 | else | ||
| 516 | ✗ | failed := true; | |
| 517 | new_exp := exp; | ||
| 518 | end if; | ||
| 519 | else | ||
| 520 | 2 | failed := true; | |
| 521 | new_exp := exp; | ||
| 522 | end if; | ||
| 523 | 35 | then (new_exp, failed); | |
| 524 | |||
| 525 | else (exp, true); | ||
| 526 | end match; | ||
| 527 | end createSingleOrSample; | ||
| 528 | |||
| 529 | function createSample | ||
| 530 | input output Call call; | ||
| 531 | input output Bucket bucket; | ||
| 532 | input Iterator iter "potential iterator"; | ||
| 533 | output Boolean failed; | ||
| 534 | output Boolean clocked; | ||
| 535 | algorithm | ||
| 536 | (failed, clocked) := match (AbsynUtil.pathLastIdent(Call.functionName(call)), Call.arguments(call)) | ||
| 537 | local | ||
| 538 | Expression clock, start, interval; | ||
| 539 | TimeEvent timeEvent; | ||
| 540 | |||
| 541 | // don't create samples for clocks | ||
| 542 | case ("sample", {_, clock}) guard(Type.isClock(Expression.typeOf(clock))) then (false, true); | ||
| 543 | |||
| 544 | case ("sample", {start, interval}) algorithm | ||
| 545 | 21 | timeEvent := getOrAdd(SAMPLE(UnorderedSet.size(bucket.time_set), start, interval, iter), bucket.time_set); | |
| 546 | // add index to sample interface | ||
| 547 | 42 | call := Call.setArguments(call, {Expression.INTEGER(getIndex(timeEvent) + 1), start, interval}); | |
| 548 | then (false, false); | ||
| 549 | |||
| 550 | case ("sample", _) algorithm | ||
| 551 | ✗ | Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed for sample operator: " + Call.toString(call)}); | |
| 552 | ✗ | then fail(); | |
| 553 | |||
| 554 | // Maybe add funky sin/cos stuff here | ||
| 555 | |||
| 556 | else (true, false); | ||
| 557 | end match; | ||
| 558 | end createSample; | ||
| 559 | |||
| 560 | function createSampleTraverse | ||
| 561 | "used only for StateEvent traversal to encapsulate sample operators" | ||
| 562 | input output Expression exp "has to be LBINARY() with comparing operator or a sample CALL()"; | ||
| 563 | input output Bucket bucket "bucket containing the events"; | ||
| 564 | input Iterator iter "potential iterator"; | ||
| 565 | input Pointer<Boolean> clocked "true if its clocked and should not create an auxiliary"; | ||
| 566 | protected | ||
| 567 | Boolean c; | ||
| 568 | algorithm | ||
| 569 | exp := match exp | ||
| 570 | local | ||
| 571 | Call call; | ||
| 572 | case Expression.CALL(call = call) algorithm | ||
| 573 | 9 | (call, bucket, _, c) := createSample(call, bucket, iter); | |
| 574 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
|
9 | exp.call := call; |
| 575 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
|
9 | if c then Pointer.update(clocked, c); end if; |
| 576 | then exp; | ||
| 577 | else exp; | ||
| 578 | end match; | ||
| 579 | end createSampleTraverse; | ||
| 580 | |||
| 581 | function getOrAdd | ||
| 582 | "returns an equal time event if it already exists, otherwise adds and returns the new one" | ||
| 583 | input TimeEvent timeEvent; | ||
| 584 | input UnorderedSet<TimeEvent> time_set; | ||
| 585 | output TimeEvent result; | ||
| 586 | algorithm | ||
| 587 | result := match UnorderedSet.get(timeEvent, time_set) | ||
| 588 | case SOME(result) then result; | ||
| 589 | else algorithm | ||
| 590 | 25 | UnorderedSet.add(timeEvent, time_set); | |
| 591 | then timeEvent; | ||
| 592 | end match; | ||
| 593 | end getOrAdd; | ||
| 594 | |||
| 595 | function getIndex | ||
| 596 | input TimeEvent timeEvent; | ||
| 597 | output Integer index; | ||
| 598 | algorithm | ||
| 599 | index := match timeEvent | ||
| 600 | 7 | case SINGLE() then timeEvent.index; | |
| 601 | 79 | case SAMPLE() then timeEvent.index; | |
| 602 | end match; | ||
| 603 | end getIndex; | ||
| 604 | |||
| 605 | function setIndex | ||
| 606 | input output TimeEvent timeEvent; | ||
| 607 | input Integer index; | ||
| 608 | algorithm | ||
| 609 | timeEvent := match timeEvent | ||
| 610 | ✗ | case SINGLE() algorithm timeEvent.index := index; then timeEvent; | |
| 611 | ✗ | case SAMPLE() algorithm timeEvent.index := index; then timeEvent; | |
| 612 | else timeEvent; | ||
| 613 | end match; | ||
| 614 | end setIndex; | ||
| 615 | |||
| 616 | function convert | ||
| 617 | input TimeEvent timeEvent; | ||
| 618 | output OldBackendDAE.TimeEvent oldTimeEvent; | ||
| 619 | algorithm | ||
| 620 | oldTimeEvent := match timeEvent | ||
| 621 | // treat single time events as sample time events with maximum integer as interval | ||
| 622 | 6 | case SINGLE() then OldBackendDAE.TimeEvent.SAMPLE_TIME_EVENT( | |
| 623 | index = timeEvent.index, | ||
| 624 | startExp = Expression.toDAE(timeEvent.trigger), | ||
| 625 | intervalExp = Expression.toDAE(Expression.makeMaxValue(Type.REAL())), | ||
| 626 | iter = convertEventIterator(timeEvent.iter)); | ||
| 627 | 19 | case SAMPLE() then OldBackendDAE.TimeEvent.SAMPLE_TIME_EVENT( | |
| 628 | index = timeEvent.index, | ||
| 629 | startExp = Expression.toDAE(timeEvent.start), | ||
| 630 | intervalExp = Expression.toDAE(timeEvent.interval), | ||
| 631 | iter = convertEventIterator(timeEvent.iter)); | ||
| 632 | else algorithm | ||
| 633 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."}); | |
| 634 | ✗ | then fail(); | |
| 635 | end match; | ||
| 636 | end convert; | ||
| 637 | end TimeEvent; | ||
| 638 | |||
| 639 | uniontype StateEvent | ||
| 640 | record STATE_EVENT | ||
| 641 | Integer index "index for simcode"; | ||
| 642 | Pointer<Variable> auxiliary "auxiliary variable representing the relation"; | ||
| 643 | UnorderedSet<Pointer<Equation>> eqns "equations where the function occurs"; | ||
| 644 | end STATE_EVENT; | ||
| 645 | |||
| 646 | function toString | ||
| 647 | input StateEvent sev; | ||
| 648 | output String str = "(" + intString(sev.index) + ") " + BVariable.toString(Pointer.access(sev.auxiliary)); | ||
| 649 | end toString; | ||
| 650 | |||
| 651 | function toStringList | ||
| 652 | input list<StateEvent> events_lst; | ||
| 653 | output String str; | ||
| 654 | algorithm | ||
| 655 | ✗ | str := StringUtil.headline_4("State Events"); | |
| 656 | ✗ | if listEmpty(events_lst) then | |
| 657 | ✗ | str := str + "\t<No State Events>\n"; | |
| 658 | else | ||
| 659 | ✗ | str := str + stringDelimitList(list(toString(te) for te in events_lst), "\n"); | |
| 660 | end if; | ||
| 661 | end toStringList; | ||
| 662 | |||
| 663 | function indexGt | ||
| 664 | input tuple<Condition, StateEvent> tpl1; | ||
| 665 | input tuple<Condition, StateEvent> tpl2; | ||
| 666 | output Boolean b; | ||
| 667 | protected | ||
| 668 | StateEvent sev1, sev2; | ||
| 669 | algorithm | ||
| 670 | 75 | (_, sev1) := tpl1; | |
| 671 | 75 | (_, sev2) := tpl2; | |
| 672 | 75 | b := sev1.index > sev2.index; | |
| 673 | end indexGt; | ||
| 674 | |||
| 675 | function fromStatement | ||
| 676 | input output Statement stmt; | ||
| 677 | input Pointer<Bucket> bucket_ptr; | ||
| 678 | input Pointer<Equation> eqn; | ||
| 679 | input VariablePointers variables; | ||
| 680 | input UnorderedMap<Path, Function> funcMap; | ||
| 681 | input list<Frame> frames = {}; | ||
| 682 | algorithm | ||
| 683 | stmt := match stmt | ||
| 684 | local | ||
| 685 | ComponentRef name; | ||
| 686 | Expression range; | ||
| 687 | list<Frame> new_frames; | ||
| 688 | Iterator iter; | ||
| 689 | Statement new_stmt; | ||
| 690 | list<Statement> new_stmts; | ||
| 691 | |||
| 692 | // don't do anything for asserts, they don't cause events | ||
| 693 | case Statement.ASSERT() then stmt; | ||
| 694 | |||
| 695 | case Statement.FOR(range = SOME(range)) algorithm | ||
| 696 | new_stmts := {}; | ||
| 697 | 7 | name := ComponentRef.fromNode(stmt.iterator, Type.INTEGER()); | |
| 698 | 7 | name := BackendDAE.lowerComponentReference(name, variables); | |
| 699 | 7 | new_frames := (name, range, NONE()) :: frames; | |
| 700 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | for elem in stmt.body loop |
| 701 | 7 | new_stmt := fromStatement(elem, bucket_ptr, eqn, variables, funcMap, new_frames); | |
| 702 | // the auxiliaries of the conditions have to be computed before the statement | ||
| 703 | 7 | new_stmts := EventInfo.createAuxStatements(new_stmts, bucket_ptr, variables); | |
| 704 | new_stmts := new_stmt :: new_stmts; | ||
| 705 | end for; | ||
| 706 | 7 | stmt.body := listReverse(new_stmts); | |
| 707 | then stmt; | ||
| 708 | |||
| 709 | else algorithm | ||
| 710 | 21 | iter := Iterator.fromFrames(listReverse(frames)); | |
| 711 | 21 | stmt := Statement.mapExp(stmt, function Expression.fakeMap( | |
| 712 | func = function collectEventsTraverse( | ||
| 713 | bucket_ptr = bucket_ptr, | ||
| 714 | iter = iter, | ||
| 715 | eqn = eqn, | ||
| 716 | funcMap = funcMap, | ||
| 717 | createEqn = false, mathEvents = true))); | ||
| 718 | then stmt; | ||
| 719 | end match; | ||
| 720 | end fromStatement; | ||
| 721 | |||
| 722 | function create | ||
| 723 | input output Expression exp; | ||
| 724 | input output Bucket bucket; | ||
| 725 | input Iterator iter; | ||
| 726 | input Pointer<Equation> eqn; | ||
| 727 | input Boolean createEqn; | ||
| 728 | protected | ||
| 729 | Condition condition; | ||
| 730 | Option<StateEvent> sev_opt; | ||
| 731 | StateEvent sev; | ||
| 732 | Pointer<Variable> aux_var; | ||
| 733 | ComponentRef aux_cref = ComponentRef.EMPTY(); | ||
| 734 | Pointer<Boolean> clocked = Pointer.create(false); | ||
| 735 | algorithm | ||
| 736 | // collect possible sample events from exp | ||
| 737 | 100 | (exp, bucket) := Expression.mapFold(exp, function TimeEvent.createSampleTraverse(iter = iter, clocked = clocked), bucket); | |
| 738 | |||
| 739 |
2/2✓ Branch 0 taken 89 times.
✓ Branch 1 taken 11 times.
|
100 | if createEqn then |
| 740 | // create an equation | ||
| 741 | 89 | condition := Condition.CONDITION(exp, iter, 0); | |
| 742 | else | ||
| 743 | // create a statement inside algorithms | ||
| 744 | 11 | condition := Condition.CONDITION(exp, iter, bucket.stmt_index); | |
| 745 | 11 | bucket.stmt_index := bucket.stmt_index + 1; | |
| 746 | end if; | ||
| 747 | |||
| 748 | 100 | sev_opt := UnorderedMap.get(condition, bucket.state_map); | |
| 749 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 87 times.
✓ Branch 3 taken 13 times.
|
100 | if isSome(sev_opt) then |
| 750 | // if the state event already exist update the equations it belongs to | ||
| 751 | 13 | SOME(sev) := sev_opt; | |
| 752 | 13 | UnorderedSet.add(eqn, sev.eqns); | |
| 753 | 13 | UnorderedMap.add(condition, sev, bucket.state_map); | |
| 754 | // return the auxiliary instead of the zero crossing | ||
| 755 | 13 | aux_cref := BVariable.getVarName(sev.auxiliary); | |
| 756 | 13 | exp := Expression.fromCref(aux_cref); | |
| 757 | elseif not Pointer.access(clocked) then | ||
| 758 | // make a new auxiliary variable and return the expression which replaces the zero crossing | ||
| 759 | 82 | (aux_var, aux_cref) := BVariable.makeEventVar(NBVariable.STATE_EVENT_STR, UnorderedMap.size(bucket.state_map), Expression.typeOf(exp), iter); | |
| 760 | 82 | exp := Expression.fromCref(aux_cref); | |
| 761 | |||
| 762 | // add the new event to the map. sev.index is the BASE of a reserved, consecutive | ||
| 763 | // block of storedRelations[] slots: one per relation of the condition and scalar | ||
| 764 | // iteration (Condition.numRelations) -- not just +1 per distinct condition -- so a | ||
| 765 | // for-loop-wrapped relation (e.g. v_abc[i] > a for i in 1:3) gets one slot per | ||
| 766 | // iteration instead of every iteration colliding on the same slot (see | ||
| 767 | // StateEvent.convert, which reads this back out to build DAE.RELATION's | ||
| 768 | // optionExpisASUB), and (a > b or c > d) gets one slot per relation. | ||
| 769 | 82 | sev := STATE_EVENT(bucket.relation_index, aux_var, UnorderedSet.fromList({eqn}, Equation.hash, Equation.equalName)); | |
| 770 | 82 | condition := Condition.setRelationIndex(condition, sev.index); | |
| 771 | 82 | bucket.relation_index := bucket.relation_index + Condition.numRelations(condition); | |
| 772 | 82 | UnorderedMap.add(condition, sev, bucket.state_map); | |
| 773 | end if; | ||
| 774 | |||
| 775 |
3/4✓ Branch 0 taken 89 times.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
|
100 | if not (createEqn or Pointer.access(clocked)) then |
| 776 | 22 | bucket.aux_stmts := SOME((condition, aux_cref) :: Util.getOptionOrDefault(bucket.aux_stmts, {})); | |
| 777 | end if; | ||
| 778 | end create; | ||
| 779 | |||
| 780 | function asubTuple | ||
| 781 | "for a SINGLE for-loop iterator with a literal-integer range (the common case, e.g. | ||
| 782 | 1:3), builds the (iterator, istart, istep) tuple DAE.RELATION's optionExpisASUB | ||
| 783 | needs so CodegenCFunctions.tpl's zero-crossing template can compute a for-loop-body | ||
| 784 | relation's actual, per-iteration storedRelations[] slot at runtime as | ||
| 785 | rel.index + (iterator - istart)/istep, instead of every iteration colliding on the | ||
| 786 | relation's single base index (see StateEvent.create). Anything else (EMPTY, NESTED, | ||
| 787 | or a non-literal-integer range) is left unhandled (NONE()) -- matching the scope of | ||
| 788 | the analogous case in the old backend's FindZeroCrossings.mo, which likewise only | ||
| 789 | ever handles a single literal-integer DAE.RANGE iterator." | ||
| 790 | input Iterator iter; | ||
| 791 | output Option<tuple<DAE.Exp, Integer, Integer>> asub; | ||
| 792 | algorithm | ||
| 793 | asub := match iter | ||
| 794 | local | ||
| 795 | Expression start, step_exp; | ||
| 796 | case Iterator.SINGLE(range = Expression.RANGE(start = start as Expression.INTEGER(), step = SOME(step_exp as Expression.INTEGER()))) | ||
| 797 | ✗ | then SOME((Expression.toDAE(Expression.fromCref(iter.name)), start.value, step_exp.value)); | |
| 798 | case Iterator.SINGLE(range = Expression.RANGE(start = start as Expression.INTEGER(), step = NONE())) | ||
| 799 | 16 | then SOME((Expression.toDAE(Expression.fromCref(iter.name)), start.value, 1)); | |
| 800 | else NONE(); | ||
| 801 | end match; | ||
| 802 | end asubTuple; | ||
| 803 | |||
| 804 | function convert | ||
| 805 | input tuple<Condition, StateEvent> sev_tpl; | ||
| 806 | input UnorderedMap<ComponentRef, Block> equation_map; | ||
| 807 | output OldBackendDAE.ZeroCrossing oldZc; | ||
| 808 | protected | ||
| 809 | Condition cond; | ||
| 810 | StateEvent sev; | ||
| 811 | Option<list<OldSimIterator>> iter; | ||
| 812 | list<ComponentRef> eqn_names; | ||
| 813 | list<Integer> eqn_indices; | ||
| 814 | DAE.Exp relExp; | ||
| 815 | algorithm | ||
| 816 | 164 | (cond, sev) := sev_tpl; | |
| 817 | 164 | iter := convertEventIterator(cond.iter); | |
| 818 |
5/6✗ Branch 3 not taken.
✓ Branch 4 taken 190 times.
✓ Branch 5 taken 190 times.
✓ Branch 6 taken 164 times.
✓ Branch 7 taken 190 times.
✓ Branch 8 taken 164 times.
|
354 | eqn_names := list(Equation.getEqnName(eqn) for eqn guard(not Equation.isDummy(Pointer.access(eqn))) in UnorderedSet.toList(sev.eqns)); |
| 819 |
6/6✓ Branch 1 taken 6 times.
✓ Branch 2 taken 184 times.
✓ Branch 3 taken 190 times.
✓ Branch 4 taken 164 times.
✓ Branch 5 taken 184 times.
✓ Branch 6 taken 164 times.
|
354 | eqn_indices := list(Block.getIndex(UnorderedMap.getSafe(name, equation_map, sourceInfo())) for name guard(UnorderedMap.contains(name, equation_map)) in eqn_names); |
| 820 | 164 | (relExp, _) := OldExpression.traverseExpBottomUp(Expression.toDAE(cond.exp), setRelationAsub, asubTuple(cond.iter)); | |
| 821 | 164 | oldZc := OldBackendDAE.ZERO_CROSSING( | |
| 822 | index = sev.index, | ||
| 823 | relation_ = relExp, | ||
| 824 | occurEquLst = eqn_indices, | ||
| 825 | iter = iter | ||
| 826 | ); | ||
| 827 | end convert; | ||
| 828 | |||
| 829 | function convertRelations | ||
| 830 | "The relations of the state event in the order of their storedRelations[] index. | ||
| 831 | A single relation or a condition without relations is the zero crossing itself." | ||
| 832 | input tuple<Condition, StateEvent> sev_tpl; | ||
| 833 | input UnorderedMap<ComponentRef, Block> equation_map; | ||
| 834 | output list<OldBackendDAE.ZeroCrossing> oldRels; | ||
| 835 | protected | ||
| 836 | Condition cond; | ||
| 837 | list<Expression> rels; | ||
| 838 | OldBackendDAE.ZeroCrossing zc; | ||
| 839 | DAE.Exp relExp; | ||
| 840 | algorithm | ||
| 841 | 82 | (cond, _) := sev_tpl; | |
| 842 | 82 | zc := convert(sev_tpl, equation_map); | |
| 843 | 82 | rels := Condition.relations(cond.exp); | |
| 844 | oldRels := match (cond.exp, rels) | ||
| 845 | case (Expression.RELATION(), _) then {zc}; | ||
| 846 | case (_, {}) then {zc}; | ||
| 847 | else algorithm | ||
| 848 | oldRels := {}; | ||
| 849 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 13 times.
|
32 | for rel in rels loop |
| 850 | 19 | (relExp, _) := OldExpression.traverseExpBottomUp(Expression.toDAE(rel), setRelationAsub, asubTuple(cond.iter)); | |
| 851 | 19 | oldRels := OldBackendDAE.ZERO_CROSSING( | |
| 852 | index = relationIndex(rel), | ||
| 853 | relation_ = relExp, | ||
| 854 | occurEquLst = zc.occurEquLst, | ||
| 855 | iter = zc.iter) :: oldRels; | ||
| 856 | end for; | ||
| 857 | 13 | then listReverse(oldRels); | |
| 858 | end match; | ||
| 859 | end convertRelations; | ||
| 860 | |||
| 861 | function relationIndex | ||
| 862 | input Expression rel; | ||
| 863 | output Integer index; | ||
| 864 | algorithm | ||
| 865 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | Expression.RELATION(index = index) := rel; |
| 866 | end relationIndex; | ||
| 867 | |||
| 868 | function setRelationAsub | ||
| 869 | "gives the relations with storedRelations[] slots the iterator offset of the condition" | ||
| 870 | input output DAE.Exp exp; | ||
| 871 | input output Option<tuple<DAE.Exp, Integer, Integer>> asub; | ||
| 872 | algorithm | ||
| 873 | exp := match exp | ||
| 874 | 195 | case DAE.RELATION() guard(exp.index >= 0) then DAE.RELATION(exp.exp1, exp.operator, exp.exp2, exp.index, asub); | |
| 875 | else exp; | ||
| 876 | end match; | ||
| 877 | end setRelationAsub; | ||
| 878 | end StateEvent; | ||
| 879 | |||
| 880 | uniontype MathEvent | ||
| 881 | "A math function with discontinuities, e.g. floor(x) or mod(x, y). Like in the old backend it | ||
| 882 | gets the index of its mathEventsValuePre[] slots as last argument, its value only changes at | ||
| 883 | events and it is a zero crossing. Inside a for-equation the call gets one block of slots for | ||
| 884 | all iterations and the index depends on the iterators, the equation and the zero crossing | ||
| 885 | stay loops: index = first + slots * (flat zero based iteration index)." | ||
| 886 | record MATH_EVENT | ||
| 887 | Expression exp "the call with the index as last argument"; | ||
| 888 | Iterator iter "iterator of the call"; | ||
| 889 | Integer index "first mathEventsValuePre[] slot"; | ||
| 890 | Integer size "number of iterations"; | ||
| 891 | UnorderedSet<Pointer<Equation>> eqns "equations where the function occurs"; | ||
| 892 | end MATH_EVENT; | ||
| 893 | |||
| 894 | function isCandidate | ||
| 895 | "integer, floor and ceil with one argument, div and mod with two, that depend on a | ||
| 896 | continuous variable or time" | ||
| 897 | input Expression exp; | ||
| 898 | output Boolean b; | ||
| 899 | algorithm | ||
| 900 | b := match exp | ||
| 901 |
3/6✓ Branch 1 taken 5 times.
✓ Branch 2 taken 693 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
698 | case Expression.CALL() then |
| 902 | numSlots(exp) > 0 and (BackendUtil.containsContinuousVar(exp) or Expression.contains(exp, isTimeCref)); | ||
| 903 | else false; | ||
| 904 | end match; | ||
| 905 | end isCandidate; | ||
| 906 | |||
| 907 | function numSlots | ||
| 908 | "mathEventsValuePre[] slots of the call, 0 if it is no math event. mod uses one more for | ||
| 909 | its internal floor." | ||
| 910 | input Expression exp; | ||
| 911 | output Integer n = 0; | ||
| 912 | protected | ||
| 913 | Integer nargs; | ||
| 914 | Function fn; | ||
| 915 | list<Expression> args; | ||
| 916 | algorithm | ||
| 917 | n := match exp | ||
| 918 | case Expression.CALL(call = Call.TYPED_CALL(fn = fn, arguments = args)) guard(Function.isBuiltin(fn)) algorithm | ||
| 919 | 559 | nargs := listLength(args); | |
| 920 | then match AbsynUtil.pathLastIdent(Function.nameConsiderBuiltin(fn)) | ||
| 921 | case "integer" guard(nargs == 1) then 1; | ||
| 922 | case "floor" guard(nargs == 1) then 1; | ||
| 923 | case "ceil" guard(nargs == 1) then 1; | ||
| 924 | case "div" guard(nargs == 2) then 2; | ||
| 925 | case "mod" guard(nargs == 2) then 3; | ||
| 926 | else 0; | ||
| 927 | end match; | ||
| 928 | else 0; | ||
| 929 | end match; | ||
| 930 | end numSlots; | ||
| 931 | |||
| 932 | function isTimeCref | ||
| 933 | input Expression exp; | ||
| 934 | output Boolean b; | ||
| 935 | algorithm | ||
| 936 | b := match exp | ||
| 937 | ✗ | case Expression.CREF() then ComponentRef.isTime(exp.cref); | |
| 938 | else false; | ||
| 939 | end match; | ||
| 940 | end isTimeCref; | ||
| 941 | |||
| 942 | function create | ||
| 943 | "returns the call with the index of an equal existing math event or a new one. Returns the | ||
| 944 | call unchanged if the iterator is not supported, it does not trigger events then." | ||
| 945 | input output Expression exp; | ||
| 946 | input output Bucket bucket; | ||
| 947 | input Iterator iter; | ||
| 948 | input Pointer<Equation> eqn; | ||
| 949 | protected | ||
| 950 | Condition key = Condition.CONDITION(exp, iter, 0); | ||
| 951 | MathEvent mev; | ||
| 952 | Call call; | ||
| 953 | Expression offset; | ||
| 954 | Integer size, slots; | ||
| 955 | Boolean supported; | ||
| 956 | algorithm | ||
| 957 | _ := match UnorderedMap.get(key, bucket.math_map) | ||
| 958 | case SOME(mev) algorithm | ||
| 959 | ✗ | UnorderedSet.add(eqn, mev.eqns); | |
| 960 | ✗ | exp := mev.exp; | |
| 961 | then (); | ||
| 962 | else algorithm | ||
| 963 | 5 | (offset, size, supported) := iterationOffset(iter); | |
| 964 | // the wasm targets need a constant index, math functions in loops trigger no events there | ||
| 965 |
2/6✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
5 | supported := supported and (Iterator.isEmpty(iter) or not isWasmTarget()); |
| 966 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if supported then |
| 967 | 5 | slots := numSlots(exp); | |
| 968 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | Expression.CALL(call = call) := exp; |
| 969 | 20 | call := Call.setArguments(call, listAppend(Call.arguments(call), { | |
| 970 | SimplifyExp.simplify(Expression.MULTARY({Expression.INTEGER(bucket.math_index), | ||
| 971 | Expression.MULTARY({Expression.INTEGER(slots), offset}, {}, Operator.makeMul(Type.INTEGER()))}, {}, Operator.makeAdd(Type.INTEGER())))})); | ||
| 972 | 10 | mev := MATH_EVENT(Expression.CALL(call), iter, bucket.math_index, size, UnorderedSet.fromList({eqn}, Equation.hash, Equation.equalName)); | |
| 973 | 5 | UnorderedMap.add(key, mev, bucket.math_map); | |
| 974 | 5 | bucket.math_index := bucket.math_index + slots * size; | |
| 975 | 5 | exp := mev.exp; | |
| 976 | end if; | ||
| 977 | then (); | ||
| 978 | end match; | ||
| 979 | end create; | ||
| 980 | |||
| 981 | function isWasmTarget | ||
| 982 | output Boolean b = Config.simCodeTarget() == "wasm-jit" or Config.simCodeTarget() == "wasm"; | ||
| 983 | end isWasmTarget; | ||
| 984 | |||
| 985 | function iterationOffset | ||
| 986 | "zero based flat index of the current iteration and the number of iterations, the last | ||
| 987 | iterator is the fastest. Only for literal ranges with step 1 and without iterator maps." | ||
| 988 | input Iterator iter; | ||
| 989 | output Expression offset = Expression.INTEGER(0); | ||
| 990 | output Integer size = 1; | ||
| 991 | output Boolean supported = true; | ||
| 992 | protected | ||
| 993 | list<ComponentRef> names; | ||
| 994 | list<Expression> ranges; | ||
| 995 | list<Option<Iterator>> maps; | ||
| 996 | Integer start = 0, step = 1, stop = 0, n; | ||
| 997 | algorithm | ||
| 998 | 5 | (names, ranges, maps) := Iterator.getFrames(iter); | |
| 999 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
|
5 | for tpl in List.zip3(names, ranges, maps) loop |
| 1000 | supported := match tpl | ||
| 1001 | case (_, Expression.RANGE(), NONE()) guard(Expression.isLiteral(Util.tuple32(tpl))) algorithm | ||
| 1002 | ✗ | (start, step, stop) := Expression.getIntegerRange(Util.tuple32(tpl), false); | |
| 1003 | ✗ | then step == 1; | |
| 1004 | else false; | ||
| 1005 | end match; | ||
| 1006 | ✗ | if not supported then | |
| 1007 | ✗ | return; | |
| 1008 | end if; | ||
| 1009 | ✗ | n := max(stop - start + 1, 0); | |
| 1010 | // offset * n + (name - start) | ||
| 1011 | ✗ | offset := Expression.MULTARY({ | |
| 1012 | Expression.MULTARY({offset, Expression.INTEGER(n)}, {}, Operator.makeMul(Type.INTEGER())), | ||
| 1013 | Expression.fromCref(Util.tuple31(tpl)), | ||
| 1014 | Expression.INTEGER(-start)}, {}, Operator.makeAdd(Type.INTEGER())); | ||
| 1015 | ✗ | size := size * n; | |
| 1016 | end for; | ||
| 1017 | end iterationOffset; | ||
| 1018 | |||
| 1019 | function indexGt | ||
| 1020 | input MathEvent mev1; | ||
| 1021 | input MathEvent mev2; | ||
| 1022 | output Boolean b = mev1.index > mev2.index; | ||
| 1023 | end indexGt; | ||
| 1024 | |||
| 1025 | function numZeroCrossings | ||
| 1026 | input MathEvent mev; | ||
| 1027 | output Integer n = mev.size; | ||
| 1028 | end numZeroCrossings; | ||
| 1029 | |||
| 1030 | function convert | ||
| 1031 | input MathEvent mev; | ||
| 1032 | input Integer index "unique zero crossing index"; | ||
| 1033 | input UnorderedMap<ComponentRef, Block> equation_map; | ||
| 1034 | output OldBackendDAE.ZeroCrossing oldZc; | ||
| 1035 | protected | ||
| 1036 | list<ComponentRef> eqn_names; | ||
| 1037 | algorithm | ||
| 1038 |
5/6✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 5 times.
|
10 | eqn_names := list(Equation.getEqnName(eqn) for eqn guard(not Equation.isDummy(Pointer.access(eqn))) in UnorderedSet.toList(mev.eqns)); |
| 1039 |
5/6✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 5 times.
|
10 | oldZc := OldBackendDAE.ZERO_CROSSING( |
| 1040 | index = index, | ||
| 1041 | relation_ = Expression.toDAE(mev.exp), | ||
| 1042 | occurEquLst = list(Block.getIndex(UnorderedMap.getSafe(name, equation_map, sourceInfo())) for name guard(UnorderedMap.contains(name, equation_map)) in eqn_names), | ||
| 1043 | iter = convertEventIterator(mev.iter) | ||
| 1044 | ); | ||
| 1045 | end convert; | ||
| 1046 | end MathEvent; | ||
| 1047 | |||
| 1048 | uniontype CompositeEvent | ||
| 1049 | record COMPOSITE_EVENT | ||
| 1050 | Integer index; | ||
| 1051 | Pointer<Variable> auxiliary; | ||
| 1052 | end COMPOSITE_EVENT; | ||
| 1053 | |||
| 1054 | function toString | ||
| 1055 | input CompositeEvent cev; | ||
| 1056 | output String str = "(" + intString(cev.index) + ") " + BVariable.pointerToString(cev.auxiliary); | ||
| 1057 | end toString; | ||
| 1058 | |||
| 1059 | function indexGt | ||
| 1060 | input tuple<Condition, CompositeEvent> tpl1; | ||
| 1061 | input tuple<Condition, CompositeEvent> tpl2; | ||
| 1062 | output Boolean b; | ||
| 1063 | protected | ||
| 1064 | CompositeEvent cev1, cev2; | ||
| 1065 | algorithm | ||
| 1066 | 34 | (_, cev1) := tpl1; | |
| 1067 | 34 | (_, cev2) := tpl2; | |
| 1068 | 34 | b := cev1.index > cev2.index; | |
| 1069 | end indexGt; | ||
| 1070 | |||
| 1071 | function create | ||
| 1072 | "Find special events of the form: sample(t0, dt) and (f(x) > 0) | ||
| 1073 | These events can only occur at the sample times. At that time the additional condition | ||
| 1074 | is checked only once, no state event necessary! | ||
| 1075 | NOTE: This does not work for SIMPLE_TIME, e.g. (time > 0.2) and (f(x) > 0)" | ||
| 1076 | input output Expression exp; | ||
| 1077 | input output Bucket bucket; | ||
| 1078 | input Iterator iter; | ||
| 1079 | input Boolean createEqn; | ||
| 1080 | output Boolean failed = false "returns true if composite event list could not be created"; | ||
| 1081 | protected | ||
| 1082 | algorithm | ||
| 1083 | (exp, bucket, failed) := match exp | ||
| 1084 | local | ||
| 1085 | Expression exp1, exp2; | ||
| 1086 | Call call; | ||
| 1087 | |||
| 1088 | // base case: sample is the left operand to AND | ||
| 1089 | case Expression.LBINARY(exp1 = exp1 as Expression.CALL(call = call), operator = Operator.OPERATOR(op = NFOperator.Op.AND)) | ||
| 1090 | guard BackendUtil.isOnlyTimeDependent(exp1) | ||
| 1091 | algorithm | ||
| 1092 | ✗ | (call, exp2, bucket, failed) := checkDirectComposite(call, exp.exp2, bucket, iter, createEqn); | |
| 1093 | ✗ | if not failed then | |
| 1094 | ✗ | exp1.call := call; | |
| 1095 | ✗ | exp.exp1 := exp1; | |
| 1096 | ✗ | if not referenceEq(exp2, exp.exp2) then | |
| 1097 | ✗ | exp.exp2 := exp2; | |
| 1098 | end if; | ||
| 1099 | end if; | ||
| 1100 | ✗ | then (exp, bucket, failed); | |
| 1101 | |||
| 1102 | // base case: sample is the right operand to AND | ||
| 1103 | case Expression.LBINARY(exp2 = exp2 as Expression.CALL(call = call), operator = Operator.OPERATOR(op = NFOperator.Op.AND)) | ||
| 1104 | guard BackendUtil.isOnlyTimeDependent(exp2) | ||
| 1105 | algorithm | ||
| 1106 | ✗ | (call, exp1, bucket, failed) := checkDirectComposite(call, exp.exp1, bucket, iter, createEqn); | |
| 1107 | ✗ | if not failed then | |
| 1108 | ✗ | exp2.call := call; | |
| 1109 | ✗ | exp.exp2 := exp2; | |
| 1110 | ✗ | if not referenceEq(exp1, exp.exp1) then | |
| 1111 | ✗ | exp.exp1 := exp1; | |
| 1112 | end if; | ||
| 1113 | end if; | ||
| 1114 | ✗ | then (exp, bucket, failed); | |
| 1115 | |||
| 1116 | // recursion: sample might be nested (all parent operators have to be AND) | ||
| 1117 | // e.g. (sample(t0, dt) and f1(x)) and f2(x) | ||
| 1118 | case Expression.LBINARY(operator = Operator.OPERATOR(op = NFOperator.Op.AND)) | ||
| 1119 | algorithm | ||
| 1120 | 20 | (exp1, bucket, failed) := create(exp.exp1, bucket, iter, createEqn); | |
| 1121 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
|
20 | if not failed then |
| 1122 | ✗ | exp.exp1 := exp1; | |
| 1123 | ✗ | (exp2, bucket, failed) := create(exp.exp2, bucket, iter, createEqn); | |
| 1124 | ✗ | if not failed then | |
| 1125 | // TODO what if there is more than one sample()? | ||
| 1126 | ✗ | exp.exp2 := exp2; | |
| 1127 | end if; | ||
| 1128 | ✗ | failed := false; // we know we have a composite time event in the first half | |
| 1129 | else | ||
| 1130 | 20 | (exp2, bucket, failed) := create(exp.exp2, bucket, iter, createEqn); | |
| 1131 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
|
20 | if not failed then |
| 1132 | ✗ | exp.exp2 := exp2; | |
| 1133 | end if; | ||
| 1134 | end if; | ||
| 1135 | 20 | then (exp, bucket, failed); | |
| 1136 | |||
| 1137 | 85 | else (exp, bucket, true); | |
| 1138 | end match; | ||
| 1139 | |||
| 1140 |
1/2✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
|
105 | if not failed then |
| 1141 | ✗ | (exp, bucket) := add(exp, iter, bucket, createEqn); | |
| 1142 | end if; | ||
| 1143 | end create; | ||
| 1144 | |||
| 1145 | function checkDirectComposite | ||
| 1146 | "Checks if call is a sample call and if it is creates the appropriate events. | ||
| 1147 | Also checks the rest exp for composite events, not sure if this is necessary." | ||
| 1148 | input output Call call "sample call"; | ||
| 1149 | input output Expression exp; | ||
| 1150 | input output Bucket bucket; | ||
| 1151 | input Iterator iter; | ||
| 1152 | input Boolean createEqn; | ||
| 1153 | output Boolean failed; | ||
| 1154 | protected | ||
| 1155 | Boolean failed2; | ||
| 1156 | algorithm | ||
| 1157 | ✗ | (call, bucket, failed, _) := TimeEvent.createSample(call, bucket, iter); | |
| 1158 | ✗ | if not failed then | |
| 1159 | ✗ | (exp, bucket, failed2) := create(exp, bucket, iter, createEqn); | |
| 1160 | if not failed2 then | ||
| 1161 | // TODO what if there is more than one sample()? Can we simplify this? | ||
| 1162 | end if; | ||
| 1163 | end if; | ||
| 1164 | end checkDirectComposite; | ||
| 1165 | |||
| 1166 | function add | ||
| 1167 | input Expression cond; | ||
| 1168 | input Iterator iter; | ||
| 1169 | output Expression exp; | ||
| 1170 | input output Bucket bucket; | ||
| 1171 | input Boolean createEqn; | ||
| 1172 | protected | ||
| 1173 | Condition condition; | ||
| 1174 | Option<CompositeEvent> cev_opt; | ||
| 1175 | CompositeEvent cev; | ||
| 1176 | Pointer<Variable> aux_var; | ||
| 1177 | ComponentRef aux_cref; | ||
| 1178 | algorithm | ||
| 1179 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 6 times.
|
36 | if createEqn then |
| 1180 | // create an equation | ||
| 1181 | 30 | condition := Condition.CONDITION(cond, iter, 0); | |
| 1182 | else | ||
| 1183 | // create a statement inside algorithms | ||
| 1184 | 6 | condition := Condition.CONDITION(cond, iter, bucket.stmt_index); | |
| 1185 | 6 | bucket.stmt_index := bucket.stmt_index + 1; | |
| 1186 | end if; | ||
| 1187 | |||
| 1188 | 36 | cev_opt := UnorderedMap.get(condition, bucket.time_map); | |
| 1189 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 2 times.
|
36 | if isSome(cev_opt) then |
| 1190 | // time event already exists, just get the identifier | ||
| 1191 | 2 | SOME(cev) := cev_opt; | |
| 1192 | 2 | aux_cref := BVariable.getVarName(cev.auxiliary); | |
| 1193 | 2 | exp := Expression.fromCref(aux_cref); | |
| 1194 | else | ||
| 1195 | // make a new auxiliary variable and return the expression which replaces the zero crossing | ||
| 1196 | 34 | (aux_var, aux_cref) := BVariable.makeEventVar(NBVariable.TIME_EVENT_STR, UnorderedMap.size(bucket.time_map), Expression.typeOf(condition.exp), iter); | |
| 1197 | 34 | exp := Expression.fromCref(aux_cref); | |
| 1198 | // add the new event to the map | ||
| 1199 | 34 | cev := CompositeEvent.COMPOSITE_EVENT(UnorderedMap.size(bucket.time_map), aux_var); | |
| 1200 | 34 | UnorderedMap.add(condition, cev, bucket.time_map); | |
| 1201 | end if; | ||
| 1202 | |||
| 1203 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 6 times.
|
36 | if not createEqn then |
| 1204 | 12 | bucket.aux_stmts := SOME((condition, aux_cref) :: Util.getOptionOrDefault(bucket.aux_stmts, {})); | |
| 1205 | end if; | ||
| 1206 | end add; | ||
| 1207 | end CompositeEvent; | ||
| 1208 | |||
| 1209 | uniontype Condition | ||
| 1210 | record CONDITION | ||
| 1211 | Expression exp; | ||
| 1212 | Iterator iter; | ||
| 1213 | Integer stmt_index; | ||
| 1214 | end CONDITION; | ||
| 1215 | |||
| 1216 | function toString | ||
| 1217 | input Condition cond; | ||
| 1218 | output String str; | ||
| 1219 | algorithm | ||
| 1220 | 275 | str := Expression.toString(cond.exp); | |
| 1221 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 263 times.
|
275 | if not Iterator.isEmpty(cond.iter) then |
| 1222 | 12 | str := str + " for {" + Iterator.toString(cond.iter) + "}"; | |
| 1223 | end if; | ||
| 1224 |
2/2✓ Branch 0 taken 241 times.
✓ Branch 1 taken 34 times.
|
275 | if not cond.stmt_index == 0 then |
| 1225 | 34 | str := str + "(" + intString(cond.stmt_index) + ")"; | |
| 1226 | end if; | ||
| 1227 | end toString; | ||
| 1228 | |||
| 1229 | function hash | ||
| 1230 | input Condition cond; | ||
| 1231 | output Integer h = stringHashDjb2(toString(cond)); | ||
| 1232 | end hash; | ||
| 1233 | |||
| 1234 | function isEqual | ||
| 1235 | input Condition cond1; | ||
| 1236 | input Condition cond2; | ||
| 1237 | output Boolean b = Expression.isEqual(cond1.exp, cond2.exp) and Iterator.isEqual(cond1.iter, cond2.iter) and cond1.stmt_index == cond2.stmt_index; | ||
| 1238 | end isEqual; | ||
| 1239 | |||
| 1240 | function size | ||
| 1241 | input Condition cond; | ||
| 1242 | output Integer s = Iterator.size(cond.iter); | ||
| 1243 | end size; | ||
| 1244 | |||
| 1245 | function setRelationIndex | ||
| 1246 | "Gives every relation of the condition, e.g. both of (a > b or c > d), a block of | ||
| 1247 | storedRelations[] slots starting at index, one slot per iteration. The relations | ||
| 1248 | then use hysteresis in the zero crossing and in the auxiliary equation." | ||
| 1249 | input output Condition cond; | ||
| 1250 | input Integer index; | ||
| 1251 | algorithm | ||
| 1252 | 82 | cond.exp := indexRelations(cond.exp, Condition.size(cond), Pointer.create(index)); | |
| 1253 | end setRelationIndex; | ||
| 1254 | |||
| 1255 | function numRelations | ||
| 1256 | "number of storedRelations[] slots of the condition. A condition without relations | ||
| 1257 | still takes the slots of one relation." | ||
| 1258 | input Condition cond; | ||
| 1259 | output Integer n = Condition.size(cond) * max(1, listLength(Condition.relations(cond.exp))); | ||
| 1260 | end numRelations; | ||
| 1261 | |||
| 1262 | function relations | ||
| 1263 | "the relations of a condition in the order of their index" | ||
| 1264 | input Expression exp; | ||
| 1265 | output list<Expression> rels; | ||
| 1266 | protected | ||
| 1267 | Pointer<list<Expression>> acc = Pointer.create({}); | ||
| 1268 | algorithm | ||
| 1269 | 328 | collectRelations(exp, acc); | |
| 1270 | 328 | rels := listReverse(Pointer.access(acc)); | |
| 1271 | end relations; | ||
| 1272 | |||
| 1273 | function indexRelations | ||
| 1274 | input output Expression exp; | ||
| 1275 | input Integer size; | ||
| 1276 | input Pointer<Integer> next; | ||
| 1277 | algorithm | ||
| 1278 | exp := match exp | ||
| 1279 | case Expression.RELATION() algorithm | ||
| 1280 | 88 | exp.index := Pointer.access(next); | |
| 1281 | 88 | Pointer.update(next, exp.index + size); | |
| 1282 | then exp; | ||
| 1283 | // relations inside noEvent() do not cause events | ||
| 1284 | case Expression.CALL() guard(Call.isNamed(exp.call, "noEvent")) then exp; | ||
| 1285 | 27 | else Expression.mapShallow(exp, function indexRelations(size = size, next = next)); | |
| 1286 | end match; | ||
| 1287 | end indexRelations; | ||
| 1288 | |||
| 1289 | function collectRelations | ||
| 1290 | "has to traverse in the same order as indexRelations" | ||
| 1291 | input output Expression exp; | ||
| 1292 | input Pointer<list<Expression>> acc; | ||
| 1293 | algorithm | ||
| 1294 | exp := match exp | ||
| 1295 | case Expression.RELATION() algorithm | ||
| 1296 | 704 | Pointer.update(acc, exp :: Pointer.access(acc)); | |
| 1297 | then exp; | ||
| 1298 | case Expression.CALL() guard(Call.isNamed(exp.call, "noEvent")) then exp; | ||
| 1299 | 108 | else Expression.mapShallow(exp, function collectRelations(acc = acc)); | |
| 1300 | end match; | ||
| 1301 | end collectRelations; | ||
| 1302 | end Condition; | ||
| 1303 | |||
| 1304 | function convertEventIterator | ||
| 1305 | input Iterator iter; | ||
| 1306 | output Option<list<OldSimIterator>> sim_iter; | ||
| 1307 | algorithm | ||
| 1308 |
6/6✓ Branch 1 taken 12 times.
✓ Branch 2 taken 182 times.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 12 times.
|
206 | sim_iter := if Iterator.isEmpty(iter) then NONE() else SOME(list(SimIterator.convert(it) for it in SimIterator.fromIterator(iter))); |
| 1309 | end convertEventIterator; | ||
| 1310 | |||
| 1311 | uniontype SpatialDistribution | ||
| 1312 | record SPATIAL_DISTRIBUTION | ||
| 1313 | Integer index "uniqueIndex"; | ||
| 1314 | Expression in0 "input 0"; | ||
| 1315 | Expression in1 "input 1"; | ||
| 1316 | Expression pos "current pos"; | ||
| 1317 | Expression dir "flow direction"; | ||
| 1318 | Expression initPnts "initial grid points"; | ||
| 1319 | Expression initVals "initial grid values"; | ||
| 1320 | Integer initSize "number of initial points"; | ||
| 1321 | Option<Expression> condition "guard condition of the enclosing if-branch, if any"; | ||
| 1322 | end SPATIAL_DISTRIBUTION; | ||
| 1323 | |||
| 1324 | function collect | ||
| 1325 | input output Pointer<Equation> eqn_ptr; | ||
| 1326 | input Option<Expression> condition; | ||
| 1327 | input Pointer<list<SpatialDistribution>> spatial_lst; | ||
| 1328 | protected | ||
| 1329 | Equation eqn = Pointer.access(eqn_ptr), new_eqn; | ||
| 1330 | algorithm | ||
| 1331 | new_eqn := match eqn | ||
| 1332 | // found an if-equation. capture the surrounding branch conditions | ||
| 1333 | case Equation.IF_EQUATION() algorithm | ||
| 1334 | 2 | eqn.body := collectIfBody(eqn.body, condition, spatial_lst); | |
| 1335 | then eqn; | ||
| 1336 | |||
| 1337 | // just collect the spatial distributions | ||
| 1338 | 2939 | else Equation.map(eqn, function collectExp(condition = condition, spatial_lst = spatial_lst), NONE(), Expression.fakeMap); | |
| 1339 | end match; | ||
| 1340 | |||
| 1341 | // update the equation if it changed | ||
| 1342 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2939 times.
|
2941 | if not referenceEq(eqn, new_eqn) then |
| 1343 | 2939 | Pointer.update(eqn_ptr, new_eqn); | |
| 1344 | end if; | ||
| 1345 | end collect; | ||
| 1346 | |||
| 1347 | function collectIfBody | ||
| 1348 | input output IfEquationBody body; | ||
| 1349 | input Option<Expression> condition; | ||
| 1350 | input Pointer<list<SpatialDistribution>> spatial_lst; | ||
| 1351 | protected | ||
| 1352 | Expression cond_true, cond_false; | ||
| 1353 | algorithm | ||
| 1354 | 4 | (cond_true, cond_false) := updateCondition(condition, body.condition); | |
| 1355 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
|
12 | body.then_eqns := list(collect(eqn, SOME(cond_true), spatial_lst)for eqn in body.then_eqns); |
| 1356 | body.else_if := Util.applyOption(body.else_if, function collectIfBody(condition = SOME(cond_false), spatial_lst = spatial_lst)); | ||
| 1357 | end collectIfBody; | ||
| 1358 | |||
| 1359 | function collectExp | ||
| 1360 | input output Expression exp; | ||
| 1361 | input Option<Expression> condition; | ||
| 1362 | input Pointer<list<SpatialDistribution>> spatial_lst; | ||
| 1363 | algorithm | ||
| 1364 | exp := match exp | ||
| 1365 | local | ||
| 1366 | Expression cond_true, cond_false; | ||
| 1367 | Call call; | ||
| 1368 | list<SpatialDistribution> slst; | ||
| 1369 | Integer index; | ||
| 1370 | Expression in0, in1, pos, dir, initPnts, initVals; | ||
| 1371 | |||
| 1372 | // found an if-expression. capture the surrounding branch conditions | ||
| 1373 | case Expression.IF() algorithm | ||
| 1374 | 92 | (cond_true, cond_false) := updateCondition(condition, exp.condition); | |
| 1375 | // use fakeMap and not mapShallow to make sure the topmost expression is handled as well | ||
| 1376 | 184 | exp.trueBranch := Expression.fakeMap(exp.trueBranch, function collectExp(condition = SOME(cond_true), spatial_lst = spatial_lst)); | |
| 1377 | 184 | exp.falseBranch := Expression.fakeMap(exp.falseBranch, function collectExp(condition = SOME(cond_false), spatial_lst = spatial_lst)); | |
| 1378 | then exp; | ||
| 1379 | |||
| 1380 | // found a spatial distribution | ||
| 1381 | case Expression.CALL(call = call as Call.TYPED_CALL(arguments = {in0, in1, pos, dir, initPnts as Expression.ARRAY(), initVals})) | ||
| 1382 | guard(AbsynUtil.pathString(Function.nameConsiderBuiltin(call.fn)) == "spatialDistribution") algorithm | ||
| 1383 | ✗ | slst := Pointer.access(spatial_lst); | |
| 1384 | ✗ | index := listLength(slst); | |
| 1385 | ✗ | exp.call := Call.setArguments(call, Expression.INTEGER(index) :: {in0, in1, pos, dir, initPnts, initVals}); | |
| 1386 | ✗ | Pointer.update(spatial_lst, SPATIAL_DISTRIBUTION(index, in0, in1, pos, dir, initPnts, initVals, arrayLength(initPnts.elements), condition) :: slst); | |
| 1387 | then exp; | ||
| 1388 | |||
| 1389 | // just traverse deeper | ||
| 1390 | 20531 | else Expression.mapShallow(exp, function collectExp(condition = condition, spatial_lst = spatial_lst)); | |
| 1391 | end match; | ||
| 1392 | end collectExp; | ||
| 1393 | |||
| 1394 | function updateCondition | ||
| 1395 | "updates the condition with a new condition creating a true and a false branch condition" | ||
| 1396 | input Option<Expression> condition; | ||
| 1397 | input Expression new_cond; | ||
| 1398 | output Expression cond_true; | ||
| 1399 | output Expression cond_false; | ||
| 1400 | protected | ||
| 1401 | Expression cond; | ||
| 1402 | algorithm | ||
| 1403 | (cond_true, cond_false) := match condition | ||
| 1404 | 15 | case SOME(cond) then (Expression.LBINARY(cond, Operator.makeAnd(Type.BOOLEAN()), new_cond), | |
| 1405 | Expression.LBINARY(cond, Operator.makeAnd(Type.BOOLEAN()), Expression.logicNegate(new_cond))); | ||
| 1406 | 81 | else (new_cond, Expression.logicNegate(new_cond)); | |
| 1407 | end match; | ||
| 1408 | end updateCondition; | ||
| 1409 | |||
| 1410 | function convert | ||
| 1411 | input SpatialDistribution sd; | ||
| 1412 | output OldSimCode.SpatialDistribution osd; | ||
| 1413 | algorithm | ||
| 1414 | ✗ | osd := OldSimCode.SPATIAL_DISTRIBUTION( | |
| 1415 | index = sd.index, | ||
| 1416 | in0 = Expression.toDAE(sd.in0), | ||
| 1417 | in1 = Expression.toDAE(sd.in1), | ||
| 1418 | pos = Expression.toDAE(sd.pos), | ||
| 1419 | dir = Expression.toDAE(sd.dir), | ||
| 1420 | initPnts = Expression.toDAE(sd.initPnts), | ||
| 1421 | initVals = Expression.toDAE(sd.initVals), | ||
| 1422 | initSize = sd.initSize, | ||
| 1423 | condition = if isSome(sd.condition) then SOME(Expression.toDAE(Util.getOption(sd.condition))) else NONE() | ||
| 1424 | ); | ||
| 1425 | end convert; | ||
| 1426 | |||
| 1427 | end SpatialDistribution; | ||
| 1428 | |||
| 1429 | |||
| 1430 | // ========================================================================= | ||
| 1431 | // PROTECTED UNIONTYPES AND FUNCTIONS | ||
| 1432 | // ========================================================================= | ||
| 1433 | |||
| 1434 | protected | ||
| 1435 | uniontype Bucket | ||
| 1436 | record BUCKET | ||
| 1437 | UnorderedSet<TimeEvent> time_set "tracks compact time events (SINGLE or SAMPLE)"; | ||
| 1438 | UnorderedMap<Condition, CompositeEvent> time_map "tracks full time events of the form $TEV_11 = ..."; | ||
| 1439 | UnorderedMap<Condition, StateEvent> state_map "tracks full state events of the form $SEV_4 = ..."; | ||
| 1440 | Option<list<tuple<Condition, ComponentRef>>> aux_stmts "optional statement conditions in algorithms"; | ||
| 1441 | Integer stmt_index "index to be used for unique statement auxiliaries"; | ||
| 1442 | Integer relation_index "next free storedRelations[] slot; unlike state_map's | ||
| 1443 | size (one entry per distinct, possibly for-loop-wrapped condition), this is incremented by | ||
| 1444 | Condition.size(condition) -- the condition's scalar iteration count -- so a for-loop-wrapped | ||
| 1445 | relation (e.g. v_abc[i] > a for i in 1:3) reserves one storedRelations slot per iteration | ||
| 1446 | instead of all iterations colliding on a single shared slot (see StateEvent.create/convert)"; | ||
| 1447 | UnorderedMap<Condition, MathEvent> math_map "math functions that trigger events by their call without index and iterator"; | ||
| 1448 | Integer math_index "next free mathEventsValuePre[] slot"; | ||
| 1449 | end BUCKET; | ||
| 1450 | end Bucket; | ||
| 1451 | |||
| 1452 | function eventsDefault extends Module.eventsInterface; | ||
| 1453 | protected | ||
| 1454 | Bucket bucket = BUCKET( | ||
| 1455 | time_set = UnorderedSet.new(TimeEvent.hash, TimeEvent.isEqual), | ||
| 1456 | time_map = UnorderedMap.new<CompositeEvent>(Condition.hash, Condition.isEqual), | ||
| 1457 | state_map = UnorderedMap.new<StateEvent>(Condition.hash, Condition.isEqual), | ||
| 1458 | aux_stmts = NONE(), | ||
| 1459 | stmt_index = 1, | ||
| 1460 | relation_index = 0, | ||
| 1461 | math_map = UnorderedMap.new<MathEvent>(Condition.hash, Condition.isEqual), | ||
| 1462 | math_index = 0); | ||
| 1463 | Pointer<Bucket> bucket_ptr; | ||
| 1464 | list<Pointer<Variable>> auxiliary_vars; | ||
| 1465 | list<Pointer<Equation>> auxiliary_eqns; | ||
| 1466 | Pointer<Integer> wc_cnt = Pointer.create(0); | ||
| 1467 | list<Pointer<Variable>> wc_vars; | ||
| 1468 | list<Pointer<Equation>> wc_eqns; | ||
| 1469 | Pointer<list<SpatialDistribution>> spatial_lst = Pointer.create({}); | ||
| 1470 | algorithm | ||
| 1471 | eventInfo := match (varData, eqData) | ||
| 1472 | case (BVariable.VAR_DATA_SIM(), BEquation.EQ_DATA_SIM()) algorithm | ||
| 1473 | // collect event info and replace all conditions with auxiliary variables | ||
| 1474 | 190 | bucket_ptr := Pointer.create(bucket); | |
| 1475 | 190 | EquationPointers.mapPtr(eqData.simulation, function collectEvents(bucket_ptr = bucket_ptr, variables = varData.variables, funcMap = funcMap)); | |
| 1476 | 190 | EquationPointers.mapPtr(eqData.clocked, function collectEvents(bucket_ptr = bucket_ptr, variables = varData.variables, funcMap = funcMap)); | |
| 1477 | 190 | EquationPointers.mapPtr(eqData.removed, function collectEvents(bucket_ptr = bucket_ptr, variables = varData.variables, funcMap = funcMap)); | |
| 1478 | // collect spatial distributions | ||
| 1479 | 190 | EquationPointers.mapPtr(eqData.simulation, function SpatialDistribution.collect(condition = NONE(), spatial_lst = spatial_lst)); | |
| 1480 | 190 | bucket := Pointer.access(bucket_ptr); | |
| 1481 | |||
| 1482 | 190 | (eventInfo, auxiliary_vars, auxiliary_eqns) := EventInfo.create(bucket, varData.variables, eqData.uniqueIndex, Pointer.access(spatial_lst)); | |
| 1483 | |||
| 1484 | // after event collection, simplify any remaining complex when-equation conditions | ||
| 1485 | // into plain discrete CREFs so that getBodyAttributes can process them. | ||
| 1486 | // This handles boolean expressions like (not x.u) that were not turned into | ||
| 1487 | // zero-crossings (e.g. purely discrete conditions). | ||
| 1488 | 190 | (wc_vars, wc_eqns) := simplifyWhenConditions(eqData.simulation, eqData.uniqueIndex, wc_cnt); | |
| 1489 | 190 | auxiliary_vars := listAppend(wc_vars, auxiliary_vars); | |
| 1490 | 190 | auxiliary_eqns := listAppend(wc_eqns, auxiliary_eqns); | |
| 1491 | 190 | (wc_vars, wc_eqns) := simplifyWhenConditions(eqData.clocked, eqData.uniqueIndex, wc_cnt); | |
| 1492 | 190 | auxiliary_vars := listAppend(wc_vars, auxiliary_vars); | |
| 1493 | 190 | auxiliary_eqns := listAppend(wc_eqns, auxiliary_eqns); | |
| 1494 | // also for the removed equations, e.g. when equations that only have reinit | ||
| 1495 | 190 | (wc_vars, wc_eqns) := simplifyWhenConditions(eqData.removed, eqData.uniqueIndex, wc_cnt); | |
| 1496 | 190 | auxiliary_vars := listAppend(wc_vars, auxiliary_vars); | |
| 1497 | 190 | auxiliary_eqns := listAppend(wc_eqns, auxiliary_eqns); | |
| 1498 | |||
| 1499 | // add auxiliary variables | ||
| 1500 | 190 | varData.variables := VariablePointers.addList(auxiliary_vars, varData.variables); | |
| 1501 | 190 | varData.unknowns := VariablePointers.addList(auxiliary_vars, varData.unknowns); | |
| 1502 | 190 | varData.initials := VariablePointers.addList(auxiliary_vars, varData.initials); | |
| 1503 | 190 | varData.discretes := VariablePointers.addList(auxiliary_vars, varData.discretes); | |
| 1504 | |||
| 1505 | // add auxiliary equations | ||
| 1506 | 190 | eqData.equations := EquationPointers.addList(auxiliary_eqns, eqData.equations); | |
| 1507 | 190 | eqData.simulation := EquationPointers.addList(auxiliary_eqns, eqData.simulation); | |
| 1508 | 190 | eqData.initials := EquationPointers.addList(auxiliary_eqns, eqData.initials); | |
| 1509 |
1/2✓ Branch 1 taken 190 times.
✗ Branch 2 not taken.
|
190 | eqData.discretes := EquationPointers.addList(auxiliary_eqns, eqData.discretes); |
| 1510 | then eventInfo; | ||
| 1511 | |||
| 1512 | else algorithm | ||
| 1513 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."}); | |
| 1514 | ✗ | then fail(); | |
| 1515 | end match; | ||
| 1516 | end eventsDefault; | ||
| 1517 | |||
| 1518 | function collectEvents | ||
| 1519 | "collects all events from an equation pointer." | ||
| 1520 | input output Pointer<Equation> eqn_ptr; | ||
| 1521 | input Pointer<Bucket> bucket_ptr; | ||
| 1522 | input VariablePointers variables; | ||
| 1523 | input UnorderedMap<Path, Function> funcMap; | ||
| 1524 | protected | ||
| 1525 | Equation eqn = Pointer.access(eqn_ptr), body_eqn; | ||
| 1526 | Iterator iter; | ||
| 1527 | Boolean createEqn = not Equation.isAlgorithm(eqn_ptr); | ||
| 1528 | BEquation.MapFuncExp collector; | ||
| 1529 | Algorithm alg; | ||
| 1530 | list<Statement> new_stmts; | ||
| 1531 | algorithm | ||
| 1532 | // create the traverser function | ||
| 1533 | 3289 | iter := Equation.getForIterator(eqn); | |
| 1534 |
2/2✓ Branch 0 taken 269 times.
✓ Branch 1 taken 3020 times.
|
3558 | collector := function collectEventsTraverse( |
| 1535 | bucket_ptr = bucket_ptr, | ||
| 1536 | iter = iter, | ||
| 1537 | eqn = eqn_ptr, | ||
| 1538 | funcMap = funcMap, | ||
| 1539 | createEqn = createEqn, mathEvents = true); | ||
| 1540 | |||
| 1541 | eqn := match eqn | ||
| 1542 | case Equation.ALGORITHM(alg = alg) algorithm | ||
| 1543 | new_stmts := {}; | ||
| 1544 |
2/2✓ Branch 0 taken 274 times.
✓ Branch 1 taken 269 times.
|
543 | for stmt in alg.statements loop |
| 1545 | 274 | stmt := StateEvent.fromStatement(stmt, bucket_ptr, eqn_ptr, variables, funcMap); | |
| 1546 | 274 | new_stmts := EventInfo.createAuxStatements(new_stmts, bucket_ptr, variables); | |
| 1547 | new_stmts := stmt :: new_stmts; | ||
| 1548 | end for; | ||
| 1549 | // save all the new stuff in our algorithm | ||
| 1550 | 269 | alg.statements := listReverse(new_stmts); | |
| 1551 | 269 | eqn.alg := Algorithm.setInputsOutputs(alg); | |
| 1552 |
4/4✓ Branch 0 taken 38 times.
✓ Branch 1 taken 269 times.
✓ Branch 2 taken 38 times.
✓ Branch 3 taken 269 times.
|
576 | eqn.size := sum(ComponentRef.size(out, true) for out in eqn.alg.outputs); |
| 1553 | then eqn; | ||
| 1554 | |||
| 1555 | // For when equations only map the condition and not the body | ||
| 1556 | case Equation.WHEN_EQUATION() algorithm | ||
| 1557 | 73 | eqn.body := WhenEquationBody.mapCondition(eqn.body, collector, NONE(), Expression.fakeMap); | |
| 1558 | then eqn; | ||
| 1559 | |||
| 1560 | // Also don't do it for when equations in for-equations | ||
| 1561 | case Equation.FOR_EQUATION(body = {body_eqn as Equation.WHEN_EQUATION()}) algorithm | ||
| 1562 | 1 | body_eqn.body := WhenEquationBody.mapCondition(body_eqn.body, collector, NONE(), Expression.fakeMap); | |
| 1563 | 1 | eqn.body := {body_eqn}; | |
| 1564 | then eqn; | ||
| 1565 | |||
| 1566 | // Map if equation body with this function to ensure that when equation bodies are not traversed | ||
| 1567 | case Equation.IF_EQUATION() algorithm | ||
| 1568 | 4 | eqn.body := IfEquationBody.mapEqnExpCref(eqn.body, | |
| 1569 | func = function collectEvents(bucket_ptr = bucket_ptr, variables = variables, funcMap = funcMap), | ||
| 1570 | funcExp = collector, | ||
| 1571 | funcCrefOpt = NONE(), | ||
| 1572 | mapFunc = Expression.mapReverse); | ||
| 1573 | then eqn; | ||
| 1574 | |||
| 1575 | 2944 | else Equation.map(eqn, collector, NONE(), Expression.fakeMap); | |
| 1576 | end match; | ||
| 1577 | |||
| 1578 |
2/2✓ Branch 1 taken 1072 times.
✓ Branch 2 taken 2217 times.
|
3289 | if not referenceEq(eqn, Pointer.access(eqn_ptr)) then |
| 1579 | 2217 | Pointer.update(eqn_ptr, eqn); | |
| 1580 | end if; | ||
| 1581 | end collectEvents; | ||
| 1582 | |||
| 1583 | function collectEventsTraverse | ||
| 1584 | "checks expressions if they are a zero crossing. | ||
| 1585 | can be used on any expression with Exression.mapReverse | ||
| 1586 | (reverse is necessary so the subexpressions are not traversed first)" | ||
| 1587 | input output Expression exp; | ||
| 1588 | input Pointer<Bucket> bucket_ptr; | ||
| 1589 | input Iterator iter; | ||
| 1590 | input Pointer<Equation> eqn; | ||
| 1591 | input UnorderedMap<Path, Function> funcMap; | ||
| 1592 | input Boolean createEqn; | ||
| 1593 | input Boolean mathEvents = true "false inside reductions"; | ||
| 1594 | algorithm | ||
| 1595 | exp := match exp | ||
| 1596 | local | ||
| 1597 | Bucket bucket; | ||
| 1598 | ClockKind clk; | ||
| 1599 | Expression condition, expanded; | ||
| 1600 | Call call; | ||
| 1601 | list<Frame> new_frames; | ||
| 1602 | Boolean success; | ||
| 1603 | |||
| 1604 | // logical unarys: e.g. not a | ||
| 1605 | // FIXME this is wrong for `not initial()` | ||
| 1606 | case Expression.LUNARY() algorithm | ||
| 1607 | 28 | (exp, bucket) := collectEventsCondition(exp, Pointer.access(bucket_ptr), iter, eqn, funcMap, createEqn); | |
| 1608 | 28 | Pointer.update(bucket_ptr, bucket); | |
| 1609 | then exp; | ||
| 1610 | |||
| 1611 | // logical binarys: e.g. (a and b) | ||
| 1612 | case Expression.LBINARY() algorithm | ||
| 1613 | 41 | (exp, bucket) := collectEventsCondition(exp, Pointer.access(bucket_ptr), iter, eqn, funcMap, createEqn); | |
| 1614 | 41 | Pointer.update(bucket_ptr, bucket); | |
| 1615 | then exp; | ||
| 1616 | |||
| 1617 | // relations: e.g. (a > b) | ||
| 1618 | case Expression.RELATION() algorithm | ||
| 1619 | 90 | (exp, bucket) := collectEventsCondition(exp, Pointer.access(bucket_ptr), iter, eqn, funcMap, createEqn); | |
| 1620 | 90 | Pointer.update(bucket_ptr, bucket); | |
| 1621 | then exp; | ||
| 1622 | |||
| 1623 | // sample functions | ||
| 1624 | case Expression.CALL() guard(Call.isNamed(exp.call, "sample")) algorithm | ||
| 1625 | 27 | (exp, bucket) := collectEventsCondition(exp, Pointer.access(bucket_ptr), iter, eqn, funcMap, createEqn); | |
| 1626 | 27 | Pointer.update(bucket_ptr, bucket); | |
| 1627 | then exp; | ||
| 1628 | |||
| 1629 | // samples in an array constructor, e.g. {sample(t0 + ts[i], p) for i in 1:n}, are expanded to have one | ||
| 1630 | // time event each, their start can depend on the iterator | ||
| 1631 | case Expression.CALL(call = Call.TYPED_ARRAY_CONSTRUCTOR()) guard(Expression.contains(exp, isSampleCall)) algorithm | ||
| 1632 | 4 | (expanded, success) := ExpandExp.expand(exp); | |
| 1633 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if success then |
| 1634 | 4 | expanded := collectEventsTraverse(expanded, bucket_ptr, iter, eqn, funcMap, createEqn, mathEvents); | |
| 1635 | else | ||
| 1636 | ✗ | expanded := Expression.mapShallow(exp, function collectEventsTraverse(bucket_ptr = bucket_ptr, iter = iter, eqn = eqn, funcMap = funcMap, createEqn = createEqn, mathEvents = mathEvents)); | |
| 1637 | end if; | ||
| 1638 | then expanded; | ||
| 1639 | |||
| 1640 | // event clocks | ||
| 1641 | case Expression.CLKCONST(clk = clk as ClockKind.EVENT_CLOCK(condition = condition)) algorithm | ||
| 1642 | 3 | clk.condition := collectEventsTraverse(condition, bucket_ptr, iter, eqn, funcMap, createEqn, mathEvents); | |
| 1643 | 3 | exp.clk := clk; | |
| 1644 | then exp; | ||
| 1645 | |||
| 1646 | // replace $PRE variables with auxiliaries | ||
| 1647 | // necessary if the $PRE variable is a when condition (cannot check the pre of a pre variable) | ||
| 1648 | case Expression.CALL(call = Call.TYPED_CALL(arguments = {Expression.CREF()})) guard(Call.isNamed(exp.call, "pre")) algorithm | ||
| 1649 | ✗ | (exp, bucket) := CompositeEvent.add(exp, iter, Pointer.access(bucket_ptr), createEqn); | |
| 1650 | ✗ | Pointer.update(bucket_ptr, bucket); | |
| 1651 | then exp; | ||
| 1652 | case Expression.CREF() guard(BVariable.isPrevious(BVariable.getVarPointer(exp.cref, sourceInfo()))) algorithm | ||
| 1653 | 5 | (exp, bucket) := CompositeEvent.add(exp, iter, Pointer.access(bucket_ptr), createEqn); | |
| 1654 | 5 | Pointer.update(bucket_ptr, bucket); | |
| 1655 | then exp; | ||
| 1656 | |||
| 1657 | // add the reduction iterators to the iterator used to build the condition | ||
| 1658 | // ToDo: if they are not ranges we need to normalize them | ||
| 1659 | case Expression.CALL(call = call as Call.TYPED_REDUCTION()) algorithm | ||
| 1660 |
4/4✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 8 times.
|
16 | new_frames := list((ComponentRef.fromNode(Util.tuple21(tpl), Type.INTEGER()), Util.tuple22(tpl), NONE()) for tpl in call.iters); |
| 1661 | // the iterators of reductions are no iterators of the equation, no math events inside | ||
| 1662 | 8 | call.exp := collectEventsTraverse(call.exp, bucket_ptr, Iterator.addFrames(iter, new_frames), eqn, funcMap, createEqn, false); | |
| 1663 | 8 | exp.call := call; | |
| 1664 | then exp; | ||
| 1665 | |||
| 1666 | // don't traverse noEvent() calls | ||
| 1667 | case Expression.CALL() guard(Call.isNamed(exp.call, "noEvent")) then exp; | ||
| 1668 | |||
| 1669 | // don't traverse cref subscripts | ||
| 1670 | case Expression.CREF() then exp; | ||
| 1671 | |||
| 1672 | // math functions that trigger events, e.g. floor(x), mod(x, y) | ||
| 1673 | case Expression.CALL() guard(mathEvents and MathEvent.isCandidate(exp)) algorithm | ||
| 1674 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | expanded := Expression.mapShallow(exp, function collectEventsTraverse(bucket_ptr = bucket_ptr, iter = iter, eqn = eqn, funcMap = funcMap, createEqn = createEqn, mathEvents = mathEvents)); |
| 1675 | 5 | (expanded, bucket) := MathEvent.create(expanded, Pointer.access(bucket_ptr), iter, eqn); | |
| 1676 | 5 | Pointer.update(bucket_ptr, bucket); | |
| 1677 | then expanded; | ||
| 1678 | |||
| 1679 | |||
| 1680 |
4/4✓ Branch 0 taken 11 times.
✓ Branch 1 taken 7906 times.
✓ Branch 2 taken 133 times.
✓ Branch 3 taken 7784 times.
|
8061 | else Expression.mapShallow(exp, function collectEventsTraverse( |
| 1681 | bucket_ptr = bucket_ptr, | ||
| 1682 | iter = iter, | ||
| 1683 | eqn = eqn, | ||
| 1684 | funcMap = funcMap, | ||
| 1685 | createEqn = createEqn, | ||
| 1686 | mathEvents = mathEvents)); | ||
| 1687 | end match; | ||
| 1688 | end collectEventsTraverse; | ||
| 1689 | |||
| 1690 | function isSampleCall | ||
| 1691 | input Expression exp; | ||
| 1692 | output Boolean b; | ||
| 1693 | algorithm | ||
| 1694 | b := match exp | ||
| 1695 | 19 | case Expression.CALL() then Call.isNamed(exp.call, "sample"); | |
| 1696 | else false; | ||
| 1697 | end match; | ||
| 1698 | end isSampleCall; | ||
| 1699 | |||
| 1700 | function collectEventsCondition | ||
| 1701 | "collects an expression as a zero crossing. | ||
| 1702 | has to be used with collectEventsTraverse to make sure that only | ||
| 1703 | suitable expressions are checked." | ||
| 1704 | input output Expression exp; | ||
| 1705 | input output Bucket bucket; | ||
| 1706 | input Iterator iter; | ||
| 1707 | input Pointer<Equation> eqn; | ||
| 1708 | input UnorderedMap<Path, Function> funcMap; | ||
| 1709 | input Boolean createEqn; | ||
| 1710 | protected | ||
| 1711 | Boolean failed = true; | ||
| 1712 | Expression original_exp; | ||
| 1713 | algorithm | ||
| 1714 | // try to create time event or composite time event | ||
| 1715 |
2/2✓ Branch 1 taken 57 times.
✓ Branch 2 taken 129 times.
|
186 | if BackendUtil.isOnlyTimeDependent(exp) then |
| 1716 | original_exp := exp; | ||
| 1717 | 57 | (exp, bucket, failed) := TimeEvent.create(exp, bucket, iter, eqn, funcMap, createEqn); | |
| 1718 | // SINGLE time events from RELATION expressions (e.g. time > 0.5) must also register a | ||
| 1719 | // StateEvent (zero-crossing) so that root-finding solvers like IDA can detect the | ||
| 1720 | // discontinuity via IDARootInit. The old backend always generated both a TimeEvent | ||
| 1721 | // and a zero-crossing for such relations; replicate that behaviour here. | ||
| 1722 |
2/2✓ Branch 0 taken 31 times.
✓ Branch 1 taken 26 times.
|
57 | if not failed then |
| 1723 | _ := match original_exp | ||
| 1724 | case Expression.RELATION() algorithm | ||
| 1725 | 9 | (_, bucket) := StateEvent.create(original_exp, bucket, iter, eqn, createEqn); | |
| 1726 | then (); | ||
| 1727 | else (); | ||
| 1728 | end match; | ||
| 1729 | end if; | ||
| 1730 | else | ||
| 1731 | // state/composite events require at least one continuous real variable; | ||
| 1732 | // skip purely discrete/integer conditions like (m == 1) with iterator m | ||
| 1733 |
2/2✓ Branch 1 taken 64 times.
✓ Branch 2 taken 65 times.
|
129 | if not BackendUtil.containsContinuousVar(exp) then |
| 1734 | 64 | return; | |
| 1735 | end if; | ||
| 1736 | 65 | (exp, bucket, failed) := CompositeEvent.create(exp, bucket, iter, createEqn); | |
| 1737 | end if; | ||
| 1738 | |||
| 1739 | // if it failed create state event | ||
| 1740 |
2/2✓ Branch 0 taken 31 times.
✓ Branch 1 taken 91 times.
|
122 | if failed then |
| 1741 | 91 | (exp, bucket) := StateEvent.create(exp, bucket, iter, eqn, createEqn); | |
| 1742 | end if; | ||
| 1743 | end collectEventsCondition; | ||
| 1744 | |||
| 1745 | function simplifyWhenConditions | ||
| 1746 | "Post-processing step after event collection: any when-equation condition | ||
| 1747 | that is not already a plain component reference (CREF) is extracted into a | ||
| 1748 | new discrete Boolean auxiliary variable ($WC_n) with a DISCRETE assignment | ||
| 1749 | equation. This normalises the condition so that WhenEquationBody.getBodyAttributes | ||
| 1750 | can always find a simple CREF, regardless of whether the original expression | ||
| 1751 | involved zero-crossings or was a purely discrete boolean like (not x.u)." | ||
| 1752 | input EquationPointers equations; | ||
| 1753 | input Pointer<Integer> idx; | ||
| 1754 | input Pointer<Integer> cnt "shared by all calls, the names of the auxiliary variables have to be unique"; | ||
| 1755 | output list<Pointer<Variable>> new_vars = {}; | ||
| 1756 | output list<Pointer<Equation>> new_eqns = {}; | ||
| 1757 | protected | ||
| 1758 | Pointer<list<Pointer<Variable>>> vars_ptr = Pointer.create({}); | ||
| 1759 | Pointer<list<Pointer<Equation>>> eqns_ptr = Pointer.create({}); | ||
| 1760 | algorithm | ||
| 1761 | 570 | EquationPointers.mapPtr(equations, function simplifyWhenConditionEqn( | |
| 1762 | idx = idx, cnt = cnt, vars_ptr = vars_ptr, eqns_ptr = eqns_ptr)); | ||
| 1763 | 570 | new_vars := Pointer.access(vars_ptr); | |
| 1764 | 570 | new_eqns := Pointer.access(eqns_ptr); | |
| 1765 | end simplifyWhenConditions; | ||
| 1766 | |||
| 1767 | function simplifyWhenConditionEqn | ||
| 1768 | "Worker for simplifyWhenConditions: processes a single equation pointer." | ||
| 1769 | input output Pointer<Equation> eqn_ptr; | ||
| 1770 | input Pointer<Integer> idx; | ||
| 1771 | input Pointer<Integer> cnt; | ||
| 1772 | input Pointer<list<Pointer<Variable>>> vars_ptr; | ||
| 1773 | input Pointer<list<Pointer<Equation>>> eqns_ptr; | ||
| 1774 | protected | ||
| 1775 | Equation eqn = Pointer.access(eqn_ptr); | ||
| 1776 | Equation body_eqn; | ||
| 1777 | Algorithm alg; | ||
| 1778 | algorithm | ||
| 1779 | eqn := match eqn | ||
| 1780 | case Equation.WHEN_EQUATION() algorithm | ||
| 1781 | 73 | eqn.body := simplifyWhenConditionBody(eqn.body, idx, cnt, vars_ptr, eqns_ptr); | |
| 1782 | then eqn; | ||
| 1783 | |||
| 1784 | case Equation.FOR_EQUATION(body = {body_eqn as Equation.WHEN_EQUATION()}) algorithm | ||
| 1785 | 1 | body_eqn.body := simplifyWhenConditionBody(body_eqn.body, idx, cnt, vars_ptr, eqns_ptr); | |
| 1786 | 1 | eqn.body := {body_eqn}; | |
| 1787 | then eqn; | ||
| 1788 | |||
| 1789 | // when statements of algorithms need a plain condition variable for the edge detection | ||
| 1790 | case Equation.ALGORITHM(alg = alg) algorithm | ||
| 1791 |
4/4✓ Branch 0 taken 289 times.
✓ Branch 1 taken 269 times.
✓ Branch 2 taken 289 times.
✓ Branch 3 taken 269 times.
|
827 | alg.statements := list(simplifyWhenConditionStmt(stmt, idx, cnt, vars_ptr, eqns_ptr) for stmt in alg.statements); |
| 1792 | 269 | eqn.alg := Algorithm.setInputsOutputs(alg); | |
| 1793 | then eqn; | ||
| 1794 | |||
| 1795 | else eqn; | ||
| 1796 | end match; | ||
| 1797 | |||
| 1798 |
2/2✓ Branch 1 taken 2942 times.
✓ Branch 2 taken 343 times.
|
3285 | if not referenceEq(eqn, Pointer.access(eqn_ptr)) then |
| 1799 | 343 | Pointer.update(eqn_ptr, eqn); | |
| 1800 | end if; | ||
| 1801 | end simplifyWhenConditionEqn; | ||
| 1802 | |||
| 1803 | function simplifyWhenConditionStmt | ||
| 1804 | "Replaces the non-CREF conditions of when statements, also the ones nested in if statements. | ||
| 1805 | Loops are skipped, since the condition can depend on the iterator." | ||
| 1806 | input output Statement stmt; | ||
| 1807 | input Pointer<Integer> idx; | ||
| 1808 | input Pointer<Integer> cnt; | ||
| 1809 | input Pointer<list<Pointer<Variable>>> vars_ptr; | ||
| 1810 | input Pointer<list<Pointer<Equation>>> eqns_ptr; | ||
| 1811 | algorithm | ||
| 1812 | stmt := match stmt | ||
| 1813 | local | ||
| 1814 | list<tuple<Expression, list<Statement>>> branches = {}; | ||
| 1815 | Expression cond; | ||
| 1816 | list<Statement> body; | ||
| 1817 | |||
| 1818 | case Statement.WHEN() algorithm | ||
| 1819 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
|
12 | for branch in stmt.branches loop |
| 1820 | 6 | (cond, body) := branch; | |
| 1821 | 6 | cond := simplifyWhenConditionExp(cond, idx, cnt, vars_ptr, eqns_ptr); | |
| 1822 | 6 | branches := (cond, body) :: branches; | |
| 1823 | end for; | ||
| 1824 | 6 | stmt.branches := listReverse(branches); | |
| 1825 | then stmt; | ||
| 1826 | |||
| 1827 | case Statement.IF() algorithm | ||
| 1828 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
|
10 | for branch in stmt.branches loop |
| 1829 | 5 | (cond, body) := branch; | |
| 1830 |
4/4✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
|
10 | body := list(simplifyWhenConditionStmt(s, idx, cnt, vars_ptr, eqns_ptr) for s in body); |
| 1831 | 5 | branches := (cond, body) :: branches; | |
| 1832 | end for; | ||
| 1833 | 5 | stmt.branches := listReverse(branches); | |
| 1834 | then stmt; | ||
| 1835 | |||
| 1836 | else stmt; | ||
| 1837 | end match; | ||
| 1838 | end simplifyWhenConditionStmt; | ||
| 1839 | |||
| 1840 | function simplifyWhenConditionBody | ||
| 1841 | "Recursively walks a WhenEquationBody chain and extracts any non-CREF condition." | ||
| 1842 | input output WhenEquationBody body; | ||
| 1843 | input Pointer<Integer> idx; | ||
| 1844 | input Pointer<Integer> cnt; | ||
| 1845 | input Pointer<list<Pointer<Variable>>> vars_ptr; | ||
| 1846 | input Pointer<list<Pointer<Equation>>> eqns_ptr; | ||
| 1847 | algorithm | ||
| 1848 | 124 | body.condition := simplifyWhenConditionExp(body.condition, idx, cnt, vars_ptr, eqns_ptr); | |
| 1849 | body.else_when := Util.applyOption(body.else_when, | ||
| 1850 | function simplifyWhenConditionBody(idx = idx, cnt = cnt, vars_ptr = vars_ptr, eqns_ptr = eqns_ptr)); | ||
| 1851 | end simplifyWhenConditionBody; | ||
| 1852 | |||
| 1853 | function simplifyWhenConditionExp | ||
| 1854 | "Replaces a non-CREF when-condition expression with a fresh $WC_n discrete | ||
| 1855 | variable and records the assignment equation $WC_n = exp." | ||
| 1856 | input output Expression cond; | ||
| 1857 | input Pointer<Integer> idx; | ||
| 1858 | input Pointer<Integer> cnt; | ||
| 1859 | input Pointer<list<Pointer<Variable>>> vars_ptr; | ||
| 1860 | input Pointer<list<Pointer<Equation>>> eqns_ptr; | ||
| 1861 | protected | ||
| 1862 | Pointer<Variable> aux_var; | ||
| 1863 | ComponentRef aux_cref; | ||
| 1864 | Pointer<Equation> aux_eqn; | ||
| 1865 | Integer i; | ||
| 1866 | algorithm | ||
| 1867 | cond := match cond | ||
| 1868 | // already a plain variable reference – nothing to do | ||
| 1869 | case Expression.CREF() then cond; | ||
| 1870 | |||
| 1871 | // array of conditions (e.g. when {c1, c2} then) – recurse per element | ||
| 1872 | case Expression.ARRAY() algorithm | ||
| 1873 | 30 | cond.elements := Array.map(cond.elements, | |
| 1874 | function simplifyWhenConditionExp(idx = idx, cnt = cnt, vars_ptr = vars_ptr, eqns_ptr = eqns_ptr)); | ||
| 1875 | then cond; | ||
| 1876 | |||
| 1877 | // initial() is a special built-in allowed in when-conditions – leave it | ||
| 1878 | case Expression.CALL() guard(Call.isNamed(cond.call, "initial")) then cond; | ||
| 1879 | |||
| 1880 | // any other expression: extract into $WC_n | ||
| 1881 | else algorithm | ||
| 1882 | 26 | i := Pointer.access(cnt); | |
| 1883 | 26 | Pointer.update(cnt, i + 1); | |
| 1884 | 26 | (aux_var, aux_cref) := BVariable.makeEventVar(NBVariable.WHEN_CONDITION_STR, i, | |
| 1885 | Expression.typeOf(cond)); | ||
| 1886 | 26 | aux_eqn := Equation.makeAssignment(Expression.fromCref(aux_cref), cond, idx, "WC", | |
| 1887 | Iterator.EMPTY(), | ||
| 1888 | EquationAttributes.default(EquationKind.DISCRETE, false)); | ||
| 1889 | 52 | Pointer.update(vars_ptr, aux_var :: Pointer.access(vars_ptr)); | |
| 1890 | 52 | Pointer.update(eqns_ptr, aux_eqn :: Pointer.access(eqns_ptr)); | |
| 1891 | 26 | cond := Expression.fromCref(aux_cref); | |
| 1892 | then cond; | ||
| 1893 | end match; | ||
| 1894 | end simplifyWhenConditionExp; | ||
| 1895 | |||
| 1896 | function containsTimeTraverseExp | ||
| 1897 | input output Expression exp; | ||
| 1898 | input Pointer<Boolean> b; | ||
| 1899 | algorithm | ||
| 1900 |
4/4✓ Branch 1 taken 37 times.
✓ Branch 2 taken 37 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 33 times.
|
74 | if not Pointer.access(b) and Expression.isTime(exp) then |
| 1901 | 33 | Pointer.update(b, true); | |
| 1902 | end if; | ||
| 1903 | end containsTimeTraverseExp; | ||
| 1904 | |||
| 1905 | function containsTimeTraverseCref | ||
| 1906 | input output ComponentRef cref; | ||
| 1907 | input Pointer<Boolean> b; | ||
| 1908 | algorithm | ||
| 1909 | ✗ | if not Pointer.access(b) and ComponentRef.isTime(cref) then | |
| 1910 | ✗ | Pointer.update(b, true); | |
| 1911 | end if; | ||
| 1912 | end containsTimeTraverseCref; | ||
| 1913 | |||
| 1914 | annotation(__OpenModelica_Interface="nbackend"); | ||
| 1915 | end NBEvents; | ||
| 1916 |