Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 77.6% 291 / 0 / 375
Functions: -% 0 / 1 / 1
Branches: 64.1% 177 / 0 / 276

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