Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 79.5% 198 / 0 / 249
Functions: -% 0 / 1 / 1
Branches: 71.5% 176 / 0 / 246

OMCompiler/Compiler/NBackEnd/Modules/2_Pre/NBFunctionAlias.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 NBFunctionAlias
37 "file: NBFunctionAlias.mo
38 package: NBFunctionAlias
39 description: This file contains the functions for the function alias encapsulation module.
40 "
41 public
42 import Module = NBModule;
43 protected
44 // OF imports
45 import Absyn;
46 import AbsynUtil;
47 import DAE;
48
49 // NF imports
50 import Call = NFCall;
51 import NFPrefixes;
52 import ComponentRef = NFComponentRef;
53 import Dimension = NFDimension;
54 import Expression = NFExpression;
55 import Operator = NFOperator;
56 import NFFunction.Function;
57 import Statement = NFStatement;
58 import SimplifyExp = NFSimplifyExp;
59 import Subscript = NFSubscript;
60 import Type = NFType;
61 import Variable = NFVariable;
62
63 // Backend imports
64 import BackendDAE = NBackendDAE;
65 import BEquation = NBEquation;
66 import Inline = NBInline;
67 import NBEquation.{Equation, EquationPointers, EqData, EquationAttributes, EquationKind, Iterator};
68 import Partition = NBPartition;
69 import Partitioning = NBPartitioning;
70 import NBPartitioning.BClock;
71 import Slice = NBSlice;
72 import BVariable = NBVariable;
73 import NBVariable.{VariablePointer, VariablePointers, VarData};
74
75 // Util imports
76 import List;
77 import StringUtil;
78 import UnorderedMap;
79 public
80 function main
81 "Wrapper function for any function alias introduction function. This will be
82 called during simulation and gets the corresponding subfunction from
83 Config."
84 extends Module.wrapper;
85 input Partition.Kind kind;
86 protected
87 Module.aliasInterface func;
88 algorithm
89 193 func := getModule();
90
91 bdae := match bdae
92 local
93 VarData varData "Data containing variable pointers";
94 EqData eqData "Data containing equation pointers";
95
96 case BackendDAE.MAIN(varData = varData, eqData = eqData)
97 algorithm
98
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 193 times.
193 (varData, eqData) := func(varData, eqData, kind);
99 193 bdae.varData := varData;
100 193 bdae.eqData := eqData;
101 then bdae;
102
103 case BackendDAE.HESSIAN(varData = varData, eqData = eqData)
104 algorithm
105 ✗ (varData, eqData) := func(varData, eqData, kind);
106 ✗ bdae.varData := varData;
107 ✗ bdae.eqData := eqData;
108 then bdae;
109
110 else algorithm
111 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."});
112 ✗ then fail();
113 end match;
114 end main;
115
116 function getModule
117 "Returns the module function that was chosen by the user."
118 output Module.functionAliasInterface func;
119 protected
120 String flag = "default";
121 algorithm
122 func := match flag
123 case "default" then functionAliasDefault;
124 /* ... New function alias modules have to be added here */
125 else fail();
126 end match;
127 end getModule;
128
129 function introduceSlicedStateAlias
130 "introduces alias for variables that are only partially states to ensure variables are either a state in their entirety or not a state at all.
131 to be used in DetectStates, just before the der() calls are resolved.
132 1. collect all crefs in der() calls that do not fully access the variable
133 2. check if any variable is not fully a state by combining all their sliced der() calls
134 3. replace all variables that are not fully states by alias variables and create equations"
135 extends Module.functionAliasInterface;
136 protected
137 UnorderedMap<Call_Id, Call_Aux> aux_map = UnorderedMap.new<Call_Aux>(Call_Id.hash, Call_Id.isEqual);
138 list<Pointer<Variable>> new_vars_cont = {}, new_vars_recd = {};
139 list<Pointer<Equation>> new_eqns_cont = {};
140 algorithm
141 () := match (eqData, varData)
142 local
143 UnorderedMap<ComponentRef,Indices> map = UnorderedMap.new<Indices>(ComponentRef.hash, ComponentRef.isEqual);
144 UnorderedSet<ComponentRef> set;
145 Pointer<Integer> aux_index = Pointer.create(1);
146
147 case (EqData.EQ_DATA_SIM(), VarData.VAR_DATA_SIM()) algorithm
148 // first collect all state slices
149 190 EquationPointers.map(eqData.simulation, function collectSlicedStatesAliasEquation(map = map));
150 190 set := getSlicedStatesSet(map);
151
152
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 190 times.
190 if not UnorderedSet.isEmpty(set) then
153 // replace all state slices of variables that are only partially states
154 ✗ eqData.simulation := EquationPointers.map(eqData.simulation, function introduceSlicedStateAliasEquation(set = set, map = aux_map, aux_index = aux_index));
155
156 // create new state slice variables and corresponding equations for the alias
157 ✗ (_, new_vars_cont, _, new_vars_recd, _, new_eqns_cont, _) :=
158 resolveAux(aux_map, eqData.uniqueIndex, false, {}, new_vars_cont, {}, new_vars_recd, {}, new_eqns_cont, {});
159 end if;
160 then ();
161 else ();
162 end match;
163
164 // add the new variables and equations (should only be continuous, states are still considered algebraic)
165 190 varData := VarData.addTypedList(varData, new_vars_cont, VarData.VarType.ALGEBRAIC);
166 190 varData := VarData.addTypedList(varData, new_vars_recd, VarData.VarType.RECORD);
167 190 eqData := EqData.addTypedList(eqData, new_eqns_cont, EqData.EqType.CONTINUOUS, false);
168
169 // update record children
170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 190 times.
190 for var in new_vars_recd loop
171 ✗ BackendDAE.lowerRecordChildren(var, VarData.getVariables(varData));
172 end for;
173
174 // dump if flag is set
175
1/2
✓ Branch 1 taken 190 times.
✗ Branch 2 not taken.
190 if Flags.isSet(Flags.DUMP_CSE) then
176 ✗ print(aliasListToString(UnorderedMap.toList(aux_map), Call_Id.toString, Call_Aux.toString, "Sliced State"));
177 end if;
178 end introduceSlicedStateAlias;
179
180 uniontype Call_Id
181 "key for UnorderedMap.
182 used to uniquely identify a function call"
183 record CALL_ID
184 Expression call;
185 Iterator iter;
186 // ToDo: instead of skipping when and if, one could collect these conditions
187 // and create the function call equations with them.
188 // Note: update hashing, isEqual and take into account that there can be
189 // elseif/elsewhen which need to be chained
190 // Option<Expression> when_condition
191 // Option<Expression> if_condition
192 end CALL_ID;
193
194 function toString
195 input Call_Id id;
196 output String str;
197 algorithm
198
2/2
✓ Branch 1 taken 127 times.
✓ Branch 2 taken 2036 times.
2163 str := if not Iterator.isEmpty(id.iter) then " [" + Iterator.toString(id.iter) + "]" else "";
199 2163 str := Expression.toString(id.call) + str;
200 end toString;
201
202 function hash
203 "just hashes the id based on its string representation"
204 input Call_Id id;
205 output Integer hash;
206 algorithm
207 2163 hash := stringHashDjb2(toString(id));
208 end hash;
209
210 function isEqual
211 input Call_Id id1;
212 input Call_Id id2;
213 output Boolean b;
214 algorithm
215
2/4
✓ Branch 1 taken 845 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 845 times.
845 b := Expression.isEqual(id1.call, id2.call) and Iterator.isEqual(id1.iter, id2.iter);
216 end isEqual;
217 end Call_Id;
218
219 uniontype Call_Aux
220 "value for UnorderedMap.
221 represents the auxilliary variable that will be created and has
222 the equation kind for auxilliary equation."
223 record CALL_AUX
224 Expression replacer;
225 EquationKind kind;
226 Boolean parsed;
227 end CALL_AUX;
228
229 function toString
230 input Call_Aux aux;
231 output String str = Expression.toString(aux.replacer);
232 end toString;
233
234 function getVars
235 input Call_Aux aux;
236 output list<Pointer<Variable>> vars = getVarsExp(aux.replacer);
237 protected
238 function getVarsExp
239 input Expression exp;
240 output list<Pointer<Variable>> vars;
241 algorithm
242 vars := match exp
243 case Expression.CREF(cref = ComponentRef.WILD()) then {};
244 607 case Expression.CREF() then {BVariable.getVarPointer(exp.cref, sourceInfo())};
245
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 time.
3 case Expression.TUPLE() then List.flatten(list(getVarsExp(elem) for elem in exp.elements));
246 else algorithm
247 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed because function alias auxilliary has a return type that currently cannot be parsed: " + Expression.toString(exp)});
248 ✗ then fail();
249 end match;
250 end getVarsExp;
251 end getVars;
252
253 function createName
254 input Type ty;
255 input Iterator iter;
256 input Pointer<Integer> aux_index;
257 input String aux_name;
258 input Boolean init;
259 output ComponentRef name;
260 protected
261 Type new_ty = ty;
262 list<Subscript> subs;
263 algorithm
264
2/2
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 564 times.
607 if not Iterator.isEmpty(iter) then
265 43 new_ty := Type.liftArrayRightList(ty, Iterator.dimensions(iter));
266 43 (_, name) := BVariable.makeAuxVar(aux_name, Pointer.access(aux_index), new_ty, init);
267 // add iterators to subscripts of auxilliary variable. fill with WHOLE if necessary
268 43 subs := Iterator.normalizedSubscripts(iter);
269 43 subs := Subscript.fillWithWholeLeft(subs, Type.dimensionCount(new_ty));
270 43 name := ComponentRef.mergeSubscripts(subs, name, true, true);
271 else
272 564 (_, name) := BVariable.makeAuxVar(aux_name, Pointer.access(aux_index), new_ty, init);
273 end if;
274 607 Pointer.update(aux_index, Pointer.access(aux_index) + 1);
275 end createName;
276 end Call_Aux;
277
278 protected
279 function functionAliasTplString
280 input tuple<String, String> tpl;
281 input Integer max_length;
282 output String str;
283 algorithm
284 ✗ str := Util.tuple21(tpl) + " " + StringUtil.repeat(".", max_length - stringLength(Util.tuple21(tpl))) + " " + Util.tuple22(tpl);
285 end functionAliasTplString;
286
287 function functionAliasDefault
288 extends Module.functionAliasInterface;
289 protected
290 UnorderedMap<Call_Id, Call_Aux> map = UnorderedMap.new<Call_Aux>(Call_Id.hash, Call_Id.isEqual);
291 VariablePointers variables = VarData.getVariables(varData);
292 UnorderedSet<VariablePointer> set = UnorderedSet.new(BVariable.hash, BVariable.equalName) "new iterators";
293 UnorderedMap<BClock, ComponentRef> clock_map = UnorderedMap.new<ComponentRef>(BClock.hash, BClock.isEqual), infer_map = UnorderedMap.new<ComponentRef>(BClock.hash, BClock.isEqual);
294 Pointer<Integer> aux_index = Pointer.create(1);
295 list<Pointer<Variable>> new_vars_disc = {}, new_vars_cont = {}, new_vars_init = {}, new_vars_recd = {}, new_vars_clck = {}, new_vars_infr = {};
296 list<Pointer<Equation>> new_eqns_disc = {}, new_eqns_cont = {}, new_eqns_init = {}, new_eqns_clck = {}, new_eqns_infr = {};
297 list<tuple<Call_Id, Call_Aux>> debug_lst_sim = {}, debug_lst_ini, sim_aliases = {};
298 algorithm
299 () := match (eqData, varData)
300 case (EqData.EQ_DATA_SIM(), VarData.VAR_DATA_SIM()) algorithm
301 // first collect all new functions from simulation equations
302 386 eqData.simulation := EquationPointers.map(eqData.simulation,
303 function introduceFunctionAliasEquation(map = map, variables = variables, set = set, aux_index = aux_index, eqn_index = eqData.uniqueIndex, init = false));
304
305 // also collect all new functions from removed equations
306 386 eqData.removed := EquationPointers.map(eqData.removed,
307 function introduceFunctionAliasEquation(map = map, variables = variables, set = set, aux_index = aux_index, eqn_index = eqData.uniqueIndex, init = false));
308
309 // create new simulation variables and corresponding equations for the function alias
310 193 (new_vars_disc, new_vars_cont, new_vars_init, new_vars_recd, new_eqns_disc, new_eqns_cont, new_eqns_init) :=
311 resolveAux(map, eqData.uniqueIndex, false, new_vars_disc, new_vars_cont, new_vars_init, new_vars_recd, new_eqns_disc, new_eqns_cont, new_eqns_init);
312
313 193 sim_aliases := UnorderedMap.toList(map);
314
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 193 times.
193 if Flags.isSet(Flags.DUMP_CSE) then
315 debug_lst_sim := sim_aliases;
316 end if;
317
318 // afterwards collect all functions from initial equations
319 386 eqData.initials := EquationPointers.map(eqData.initials,
320 function introduceFunctionAliasEquation(map = map, variables = variables, set = set, aux_index = aux_index, eqn_index = eqData.uniqueIndex, init = true));
321
322 // add parameter function alias
323 386 varData.parameters := VariablePointers.mapPtr(varData.parameters,
324 function BVariable.mapExp(funcExp = function introduceFunctionAlias(map = map, aux_index = aux_index, iter = Iterator.EMPTY(), init = true),
325 mapFunc = Expression.fakeMap));
326
327 // create new initialization variables and corresponding equations for the function alias
328 193 (new_vars_disc, new_vars_cont, new_vars_init, new_vars_recd, new_eqns_disc, new_eqns_cont, new_eqns_init) :=
329 resolveAux(map, eqData.uniqueIndex, true, new_vars_disc, new_vars_cont, new_vars_init, new_vars_recd, new_eqns_disc, new_eqns_cont, new_eqns_init);
330
331 // create clock alias equations
332 193 (new_eqns_clck, new_eqns_infr, new_vars_clck, new_vars_infr, clock_map, infer_map) := addClockedAlias(eqData.simulation, eqData.uniqueIndex);
333 then ();
334 else ();
335 end match;
336
337 // add the new variables and equations.
338 // Note: inferred clocks are handled as unknowns to properly partition them
339 193 varData := VarData.addTypedList(varData, new_vars_cont, VarData.VarType.ALGEBRAIC);
340 193 varData := VarData.addTypedList(varData, new_vars_disc, VarData.VarType.DISCRETE);
341 193 varData := VarData.addTypedList(varData, new_vars_init, VarData.VarType.PARAMETER);
342 193 varData := VarData.addTypedList(varData, new_vars_recd, VarData.VarType.RECORD);
343 193 varData := VarData.addTypedList(varData, new_vars_clck, VarData.VarType.CLOCK);
344 193 varData := VarData.addTypedList(varData, new_vars_infr, VarData.VarType.DISCRETE);
345 193 varData := VarData.addTypedList(varData, UnorderedSet.toList(set), VarData.VarType.ITERATOR);
346 193 eqData := EqData.addTypedList(eqData, new_eqns_cont, EqData.EqType.CONTINUOUS, false);
347 193 eqData := EqData.addTypedList(eqData, new_eqns_disc, EqData.EqType.DISCRETE, false);
348 193 eqData := EqData.addTypedList(eqData, new_eqns_init, EqData.EqType.INITIAL, false);
349 193 eqData := EqData.addTypedList(eqData, new_eqns_clck, EqData.EqType.CLOCKED, false);
350 193 eqData := EqData.addTypedList(eqData, new_eqns_infr, EqData.EqType.DISCRETE, false);
351
352 // update record children
353
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 193 times.
241 for var in new_vars_recd loop
354 48 BackendDAE.lowerRecordChildren(var, VarData.getVariables(varData));
355 end for;
356
357 // the record children exist now, so the auxiliary variables can get their start values
358
2/2
✓ Branch 0 taken 376 times.
✓ Branch 1 taken 193 times.
569 for tpl in sim_aliases loop
359 376 addAuxStartValue(Util.tuple21(tpl), Util.tuple22(tpl));
360 end for;
361
362 // dump if flag is set
363
1/2
✓ Branch 1 taken 193 times.
✗ Branch 2 not taken.
193 if Flags.isSet(Flags.DUMP_CSE) then
364 // remove sim vars from final map to see whats exclusively initial
365 ✗ for tpl in debug_lst_sim loop
366 ✗ UnorderedMap.remove(Util.tuple21(tpl), map);
367 end for;
368 ✗ debug_lst_ini := UnorderedMap.toList(map);
369 ✗ print(aliasListToString(debug_lst_sim, Call_Id.toString, Call_Aux.toString, "Simulation Function"));
370 ✗ print(aliasListToString(debug_lst_ini, Call_Id.toString, Call_Aux.toString, "Initial Function"));
371 ✗ print(aliasListToString(UnorderedMap.toList(clock_map), BClock.toString, ComponentRef.toString, "Clocked Function"));
372 ✗ print(aliasListToString(UnorderedMap.toList(infer_map), BClock.toString, ComponentRef.toString, "Inferred Clocked Function"));
373 end if;
374 end functionAliasDefault;
375
376 function addAuxStartValue
377 "The continuous outputs of a function call start at the call itself. The arguments are replaced by their
378 start values in the initialization, so a changed start value of an argument also changes the start value here.
379 Without it the outputs start at zero, which can make the Jacobian singular at the start (e.g. for products).
380 Only calls that can safely be evaluated at the start values are used."
381 input Call_Id id;
382 input Call_Aux aux;
383 algorithm
384
5/6
✓ Branch 0 taken 374 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 30 times.
✓ Branch 4 taken 344 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 30 times.
376 if aux.kind == EquationKind.CONTINUOUS and (Iterator.isEmpty(id.iter) or hasPlainIterators(id.iter)) then
385 () := match (id.call, aux.replacer)
386 local
387 Integer i = 0;
388 Expression replacer, call_exp;
389 list<Expression> elements;
390
391 case (call_exp as Expression.CALL(call = Call.TYPED_CALL()), replacer as Expression.CREF(cref = ComponentRef.CREF()))
392 guard(isStartUsefulFunction(Call.typedFunction(call_exp.call))) algorithm
393 133 setAuxStartValue(BVariable.getVarPointer(replacer.cref, sourceInfo()), iterateStart(id.call, id.iter));
394 then ();
395
396 case (call_exp as Expression.CALL(call = Call.TYPED_CALL()), Expression.TUPLE(elements = elements))
397 guard(isStartUsefulFunction(Call.typedFunction(call_exp.call))) algorithm
398
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for elem in elements loop
399 2 i := i + 1;
400 () := match elem
401 case Expression.CREF(cref = ComponentRef.CREF())
402 algorithm
403 2 setAuxStartValue(BVariable.getVarPointer(elem.cref, sourceInfo()), iterateStart(Expression.tupleElement(id.call, i), id.iter));
404 then ();
405 else ();
406 end match;
407 end for;
408 then ();
409
410 else ();
411 end match;
412 end if;
413 end addAuxStartValue;
414
415 function hasPlainIterators
416 "true if all iterators are plain ranges (no mapped array iterators)"
417 input Iterator iter;
418 output Boolean b;
419 protected
420 list<Option<Iterator>> maps;
421 algorithm
422 30 (_, _, maps) := Iterator.getFrames(iter);
423 b := true;
424
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 30 times.
66 for map in maps loop
425
3/8
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 36 times.
36 b := b and isNone(map);
426 end for;
427 end hasPlainIterators;
428
429 function iterateStart
430 "The start value of the full variable of a call inside of a for equation. The dimensions of the
431 call result come first, then the iterators: {{call[k] for i1 in r1, i2 in r2} for k in 1:n}.
432 Only scalar and vector results, EMPTY() otherwise."
433 input output Expression exp;
434 input Iterator iter;
435 protected
436 list<Dimension> dims;
437 list<Expression> elements;
438 Integer n;
439 algorithm
440
2/2
✓ Branch 1 taken 128 times.
✓ Branch 2 taken 7 times.
135 if not Iterator.isEmpty(iter) then
441 7 dims := Type.arrayDims(Expression.typeOf(exp));
442
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 if listEmpty(dims) then
443 4 exp := iterateScalarStart(exp, iter);
444 elseif listLength(dims) == 1 and Dimension.isKnown(listHead(dims)) then
445 3 n := Dimension.size(listHead(dims));
446
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
11 elements := list(iterateScalarStart(Expression.applySubscripts({Subscript.INDEX(Expression.INTEGER(k))}, exp), iter) for k in 1:n);
447
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 exp := if List.any(elements, Expression.isEmpty) then Expression.EMPTY(Expression.typeOf(exp))
448 else Expression.makeExpArray(listArray(elements), Expression.typeOf(listHead(elements)));
449 else
450 ✗ exp := Expression.EMPTY(Expression.typeOf(exp));
451 end if;
452 end if;
453 end iterateStart;
454
455 function iterateScalarStart
456 "{{exp for i2 in r2} for i1 in r1} for the iterators {i1, i2}, unrolled with literal iterator values
457 (the element crefs have to be simulation variables). EMPTY() for non constant or too big ranges."
458 input output Expression exp;
459 input Iterator iter;
460 protected
461 constant Integer max_size = 1000;
462 list<ComponentRef> names;
463 list<Expression> ranges;
464 list<Integer> values;
465 list<Expression> elements;
466 Integer size = 1;
467 Type ty;
468 algorithm
469 8 (names, ranges, _) := Iterator.getFrames(iter);
470
2/2
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 8 times.
16 for tpl in listReverse(List.zip(names, ranges)) loop
471 8 values := rangeValues(Util.tuple22(tpl));
472 8 size := size * listLength(values);
473
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if listEmpty(values) or size > max_size then
474 ✗ exp := Expression.EMPTY(Expression.typeOf(exp));
475 ✗ return;
476 end if;
477
4/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 8 times.
32 elements := list(SimplifyExp.simplify(Expression.replaceIterator(exp, ComponentRef.node(Util.tuple21(tpl)), Expression.INTEGER(v))) for v in values);
478 8 ty := Type.liftArrayLeft(Expression.typeOf(exp), Dimension.fromInteger(listLength(values)));
479 8 exp := Expression.makeArray(ty, listArray(elements));
480 end for;
481 end iterateScalarStart;
482
483 function rangeValues
484 "the values of a constant integer range, empty otherwise"
485 input Expression range;
486 output list<Integer> values = {};
487 algorithm
488 values := match range
489 local
490 Integer start, step, stop;
491 case Expression.RANGE(start = Expression.INTEGER(start), step = NONE(), stop = Expression.INTEGER(stop))
492 8 then List.intRange2(start, stop);
493 case Expression.RANGE(start = Expression.INTEGER(start), step = SOME(Expression.INTEGER(step)), stop = Expression.INTEGER(stop))
494 ✗ guard(step <> 0) then list(i for i in start:step:stop);
495 else {};
496 end match;
497 end rangeValues;
498
499 function setAuxStartValue
500 "sets the start value of a real variable (or all real children of a record) to the expression"
501 input Pointer<Variable> var_ptr;
502 input Expression exp;
503 protected
504 list<Pointer<Variable>> children = BVariable.getRecordChildren(var_ptr);
505 Variable var;
506 algorithm
507
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 108 times.
135 if listEmpty(children) then
508 108 var := Pointer.access(var_ptr);
509 // keep a start value inherited from the function output declaration.
510 // arrays (e.g. the outputs of calls in for equations) need a start value of the same size
511
8/14
✓ Branch 3 taken 108 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 108 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 108 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 29 times.
✓ Branch 13 taken 79 times.
✓ Branch 17 taken 29 times.
✗ Branch 18 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 108 times.
✓ Branch 24 taken 108 times.
✗ Branch 25 not taken.
108 if Type.isReal(Type.arrayElementType(Variable.typeOf(var))) and not BVariable.isRecord(var_ptr)
512 and not Expression.isEmpty(exp)
513 and (not BVariable.isArray(var_ptr) or Type.isEqual(Variable.typeOf(var), Expression.typeOf(exp)))
514 and isNone(BVariable.getStartAttribute(var_ptr)) then
515 108 Pointer.update(var_ptr, BVariable.setStartAttribute(var, exp));
516 end if;
517 else
518
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 27 times.
82 for child in children loop
519 55 var := Pointer.access(child);
520
6/10
✓ Branch 2 taken 54 times.
✓ Branch 3 taken 1 time.
✓ Branch 5 taken 54 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 54 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 54 times.
✓ Branch 15 taken 54 times.
✗ Branch 16 not taken.
55 if Type.isReal(Variable.typeOf(var)) and not BVariable.isArray(child) and not BVariable.isRecord(child)
521 and isNone(BVariable.getStartAttribute(child)) then
522 54 Pointer.update(child, BVariable.setStartAttribute(var, Expression.recordElement(ComponentRef.firstName(var.name), exp)));
523 end if;
524 end for;
525 end if;
526 end setAuxStartValue;
527
528 function isStartUsefulFunction
529 "builtin functions (sin, abs, ...) are cheap to iterate on and not worth a start equation"
530 input Function fn;
531 output Boolean b = not Function.isBuiltin(fn) and isStartSafeFunction(fn, 0);
532 end isStartUsefulFunction;
533
534 function isStartSafeFunction
535 "A function can be evaluated at the start values of its arguments if it can not fail: no asserts or terminates,
536 not impure or external, and all functions it calls are safe as well."
537 input Function fn;
538 input Integer depth;
539 output Boolean safe;
540 protected
541 list<Statement> body;
542 Pointer<Boolean> unsafe;
543 algorithm
544
2/4
✓ Branch 1 taken 412 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 412 times.
✗ Branch 5 not taken.
412 if Function.isBuiltin(fn) or Function.isDefaultRecordConstructor(fn) then
545 safe := true;
546 elseif depth > 4 or Function.isExternal(fn) or Function.isImpure(fn) then
547 safe := false;
548 else
549 400 body := Function.getBody(fn);
550
2/2
✓ Branch 3 taken 397 times.
✓ Branch 4 taken 3 times.
797 unsafe := Pointer.create(List.any(body, function Statement.contains(fn = isUnsafeStatement)));
551
2/2
✓ Branch 1 taken 397 times.
✓ Branch 2 taken 3 times.
400 if not Pointer.access(unsafe) then
552 397 Statement.applyExpList(body, function markUnsafeCalls(unsafe = unsafe, depth = depth));
553 end if;
554 400 safe := not Pointer.access(unsafe);
555 end if;
556 end isStartSafeFunction;
557
558 function isUnsafeStatement
559 input Statement stmt;
560 output Boolean b;
561 algorithm
562 b := match stmt
563 case Statement.ASSERT() then true;
564 case Statement.TERMINATE() then true;
565 else false;
566 end match;
567 end isUnsafeStatement;
568
569 function markUnsafeCalls
570 input Expression exp;
571 input Pointer<Boolean> unsafe;
572 input Integer depth;
573 algorithm
574
4/4
✓ Branch 2 taken 2034 times.
✓ Branch 3 taken 1006 times.
✓ Branch 6 taken 807 times.
✓ Branch 7 taken 199 times.
3040 if not Pointer.access(unsafe) and Expression.contains(exp, function isUnsafeCallExp(depth = depth)) then
575 199 Pointer.update(unsafe, true);
576 end if;
577 end markUnsafeCalls;
578
579 function isUnsafeCallExp
580 input Expression exp;
581 input Integer depth;
582 output Boolean b;
583 algorithm
584 b := match exp
585 local
586 Function fn;
587 // the generated code asserts on divisions by zero and on the domain of sqrt and log
588 102 case Expression.BINARY(operator = Operator.OPERATOR(op = NFOperator.Op.DIV)) then not Expression.isLiteral(exp.exp2);
589 ✗ case Expression.BINARY(operator = Operator.OPERATOR(op = NFOperator.Op.DIV_EW)) then not Expression.isLiteral(exp.exp2);
590 ✗ case Expression.BINARY(operator = Operator.OPERATOR(op = NFOperator.Op.DIV_SCALAR_ARRAY)) then not Expression.isLiteral(exp.exp2);
591 ✗ case Expression.BINARY(operator = Operator.OPERATOR(op = NFOperator.Op.DIV_ARRAY_SCALAR)) then not Expression.isLiteral(exp.exp2);
592 case Expression.CALL(call = Call.TYPED_CALL()) algorithm
593 234 fn := Call.typedFunction(exp.call);
594
2/2
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 184 times.
234 then if Function.isBuiltin(fn) then List.contains({"sqrt", "log", "log10"}, AbsynUtil.pathLastIdent(Function.name(fn)), stringEq)
595 else not isStartSafeFunction(fn, depth + 1);
596 else false;
597 end match;
598 end isUnsafeCallExp;
599
600 function aliasListToString<T1, T2>
601 input list<tuple<T1, T2>> aux_lst;
602 input idToString func1;
603 input auxToString func2;
604 input String name;
605 output String str;
606 protected
607 list<tuple<String, String>> str_lst;
608 Integer max_length;
609 partial function idToString<T1>
610 input T1 t1;
611 output String str;
612 end idToString;
613 partial function auxToString<T2>
614 input T2 t2;
615 output String str;
616 end auxToString;
617 algorithm
618 ✗ str := StringUtil.headline_3(name + " Alias");
619 ✗ if listEmpty(aux_lst) then
620 ✗ str := str + " <no alias>\n\n";
621 else
622 ✗ str_lst := list((func2(Util.tuple22(tpl)), func1(Util.tuple21(tpl))) for tpl in aux_lst);
623 ✗ max_length := max(stringLength(Util.tuple21(tpl)) for tpl in str_lst) + 3;
624 ✗ str := str + List.toStringCustom(str_lst, function functionAliasTplString(max_length = max_length), "", " ", "\n ", "\n\n");
625 end if;
626 end aliasListToString;
627
628 function resolveAux
629 input UnorderedMap<Call_Id, Call_Aux> map;
630 input Pointer<Integer> eq_index;
631 input Boolean init;
632 input output list<Pointer<Variable>> new_vars_disc;
633 input output list<Pointer<Variable>> new_vars_cont;
634 input output list<Pointer<Variable>> new_vars_init;
635 input output list<Pointer<Variable>> new_vars_recd;
636 input output list<Pointer<Equation>> new_eqns_disc;
637 input output list<Pointer<Equation>> new_eqns_cont;
638 input output list<Pointer<Equation>> new_eqns_init;
639 protected
640 Call_Id id;
641 Call_Aux aux;
642 Boolean disc;
643 Pointer<Equation> new_eqn;
644 list<Pointer<Variable>> new_vars;
645 algorithm
646 // create new simulation variables and corresponding equations for the function alias
647
2/2
✓ Branch 2 taken 982 times.
✓ Branch 3 taken 386 times.
1368 for tpl in listReverse(UnorderedMap.toList(map)) loop
648 982 (id, aux) := tpl;
649 // only create new var and eqn if there is not already parsed
650
2/2
✓ Branch 0 taken 606 times.
✓ Branch 1 taken 376 times.
982 if not aux.parsed then
651 606 new_vars := Call_Aux.getVars(aux);
652 disc := true;
653
654 // categorize all aux variables
655
2/2
✓ Branch 0 taken 607 times.
✓ Branch 1 taken 606 times.
1213 for new_var in new_vars loop
656 607 (disc, new_vars_disc, new_vars_cont, new_vars_init, new_vars_recd) := addAuxVar(new_var, disc, new_vars_disc, new_vars_cont, new_vars_init, new_vars_recd, init);
657 end for;
658
659 // if any of the created variables is continuous, so is the equation
660 606 new_eqn := Equation.makeAssignment(aux.replacer, id.call, eq_index, "AUX", id.iter, EquationAttributes.default(aux.kind, init));
661
2/2
✓ Branch 0 taken 230 times.
✓ Branch 1 taken 376 times.
606 if init then
662 new_eqns_init := new_eqn :: new_eqns_init;
663 elseif disc then
664 new_eqns_disc := new_eqn :: new_eqns_disc;
665 else
666 new_eqns_cont := new_eqn :: new_eqns_cont;
667 end if;
668
669 606 aux.parsed := true;
670 606 UnorderedMap.add(id, aux, map);
671 end if;
672 end for;
673 end resolveAux;
674
675 function introduceFunctionAliasEquation
676 "creates auxilliary variables for all not inlineable function calls in the equation"
677 input output Equation eqn;
678 input UnorderedMap<Call_Id, Call_Aux> map;
679 input VariablePointers variables;
680 input UnorderedSet<VariablePointer> set "new iterators";
681 input Pointer<Integer> aux_index;
682 input Pointer<Integer> eqn_index;
683 input Boolean init;
684 protected
685 Iterator iter;
686 type Depth = enumeration(FULL, CONDITION, STOP);
687 Depth depth;
688 algorithm
689 // inline trivial array constructors first
690 4497 eqn := Inline.inlineArrayConstructorSingle(eqn, Iterator.EMPTY(), variables, set, eqn_index);
691
692 // get iterator and determine if it needs to be checked further
693 (iter, depth) := match eqn
694 local
695 Equation body;
696
4/4
✓ Branch 2 taken 268 times.
✓ Branch 3 taken 1 time.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 265 times.
269 case Equation.FOR_EQUATION(body = {body}) then (eqn.iter, if Equation.isWhenEquation(Pointer.create(body))
697 or Equation.isIfEquation(Pointer.create(body))
698 then Depth.CONDITION else Depth.FULL);
699 case Equation.WHEN_EQUATION() then (Iterator.EMPTY(), Depth.CONDITION);
700 case Equation.IF_EQUATION() then (Iterator.EMPTY(), Depth.CONDITION);
701 case Equation.ALGORITHM() then (Iterator.EMPTY(), Depth.STOP);
702 else (Iterator.EMPTY(), Depth.FULL);
703 end match;
704
705 // do the function alias replacement
706
2/2
✓ Branch 0 taken 265 times.
✓ Branch 1 taken 4 times.
269 if depth == Depth.FULL then
707
2/2
✓ Branch 0 taken 4053 times.
✓ Branch 1 taken 70 times.
8176 eqn := Equation.map(eqn, function introduceFunctionAlias(map = map, aux_index = aux_index, iter = iter, init = init), NONE(), Expression.fakeMap);
708 elseif depth == Depth.CONDITION then
709
1/2
✓ Branch 0 taken 83 times.
✗ Branch 1 not taken.
166 eqn := Equation.mapCondition(eqn, function introduceFunctionAlias(map = map, aux_index = aux_index, iter = iter, init = init), NONE(), Expression.fakeMap);
710 end if;
711 end introduceFunctionAliasEquation;
712
713 function introduceFunctionAlias
714 "checks if an expression is a function call and replaces it with auxilliary if not inlinable
715 map with Equation.map() or Expression.map()
716 ToDo: also exclude special functions der(), pre(), ..."
717 input output Expression exp;
718 input UnorderedMap<Call_Id, Call_Aux> map;
719 input Pointer<Integer> aux_index;
720 input Iterator iter;
721 input Boolean init;
722 protected
723 Iterator deep_iter;
724 algorithm
725 // add local iterators to deep recursion
726 deep_iter := match exp
727 1395 case Expression.CALL() then Iterator.expand(iter, exp.call);
728 else iter;
729 end match;
730
2/2
✓ Branch 0 taken 22220 times.
✓ Branch 1 taken 6064 times.
50504 exp := Expression.mapShallow(exp, function introduceFunctionAlias(map = map, aux_index = aux_index, iter = deep_iter, init = init));
731
732 // use the original iterator for local analysis
733 exp := match exp
734 local
735 Call call;
736 Expression new_exp, sub_exp;
737
738 660 case Expression.CALL() guard(checkCallReplacement(exp.call)) then introduceAlias(exp, map, aux_index, NBVariable.FUNCTION_STR, iter, init);
739
740 // create alias for array constructors as arguments to functions
741 case new_exp as Expression.CALL(call = call as Call.TYPED_CALL()) algorithm
742
4/4
✓ Branch 0 taken 944 times.
✓ Branch 1 taken 635 times.
✓ Branch 2 taken 944 times.
✓ Branch 3 taken 635 times.
2214 call.arguments := list(Expression.map(arg, function introduceArrayConstructorAlias(map = map, aux_index = aux_index, iter = iter, init = init)) for arg in call.arguments);
743 635 new_exp.call := call;
744 then new_exp;
745
746 // create alias for array constructors in multaries and binaries
747 // Note: do not map! only replace top lvl constructors
748 case Expression.MULTARY() algorithm
749
4/4
✓ Branch 0 taken 6860 times.
✓ Branch 1 taken 3772 times.
✓ Branch 2 taken 6860 times.
✓ Branch 3 taken 3772 times.
14404 exp.arguments := list(introduceArrayConstructorAlias(arg, map, aux_index, iter, init) for arg in exp.arguments);
750
4/4
✓ Branch 0 taken 989 times.
✓ Branch 1 taken 3772 times.
✓ Branch 2 taken 989 times.
✓ Branch 3 taken 3772 times.
8533 exp.inv_arguments := list(introduceArrayConstructorAlias(arg, map, aux_index, iter, init) for arg in exp.inv_arguments);
751 then exp;
752 case Expression.BINARY() algorithm
753 245 exp.exp1 := introduceArrayConstructorAlias(exp.exp1, map, aux_index, iter, init);
754 245 exp.exp2 := introduceArrayConstructorAlias(exp.exp2, map, aux_index, iter, init);
755 then exp;
756
757 // remove tuple expressions that occur when using a function only for one output
758 // y = fun(x)[1] where fun() has multiple outputs
759 // we create y = ($FUN1, $FUN2)[1] and simplify to y = $FUN1
760 case Expression.TUPLE_ELEMENT(tupleExp = sub_exp as Expression.TUPLE()) algorithm
761 ✗ if exp.index > listLength(sub_exp.elements) then
762 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed to get subscripted tuple element: " + Expression.toString(exp)});
763 ✗ fail();
764 else
765 ✗ new_exp := listGet(sub_exp.elements, exp.index);
766 end if;
767 then new_exp;
768
769 // do nothing if not function call or inlineable
770 else exp;
771 end match;
772 end introduceFunctionAlias;
773
774 function introduceArrayConstructorAlias
775 "introduces alias variables for array constructor and reduction calls"
776 input output Expression exp;
777 input UnorderedMap<Call_Id, Call_Aux> map;
778 input Pointer<Integer> aux_index;
779 input Iterator iter;
780 input Boolean init;
781 algorithm
782 exp := match exp
783 5 case Expression.CALL(call = Call.TYPED_ARRAY_CONSTRUCTOR()) then introduceAlias(exp, map, aux_index, NBVariable.FUNCTION_STR, iter, init);
784 ✗ case Expression.CALL(call = Call.TYPED_REDUCTION()) then introduceAlias(exp, map, aux_index, NBVariable.FUNCTION_STR, iter, init);
785 else exp;
786 end match;
787 end introduceArrayConstructorAlias;
788
789 function introduceAliasCrefConditional
790 "introduces alias variables for crefs, only if they are in the set"
791 input output Expression exp;
792 input UnorderedSet<ComponentRef> set;
793 input UnorderedMap<Call_Id, Call_Aux> map;
794 input Pointer<Integer> aux_index;
795 input Iterator iter;
796 input Boolean init;
797 algorithm
798 exp := match exp
799 case Expression.CREF() guard(UnorderedSet.contains(ComponentRef.stripSubscriptsAll(exp.cref), set))
800 ✗ then introduceAlias(exp, map, aux_index, NBVariable.STATE_ALIAS_STR, iter, init);
801 else exp;
802 end match;
803 end introduceAliasCrefConditional;
804
805 function introduceAlias
806 "introduces alias variables for any expression.
807 Extra handling for cat() and promotion() calls."
808 input output Expression exp;
809 input UnorderedMap<Call_Id, Call_Aux> map;
810 input Pointer<Integer> aux_index;
811 input String aux_name;
812 input Iterator iter;
813 input Boolean init;
814 protected
815 list<ComponentRef> names;
816 list<Expression> ranges;
817 list<Option<Iterator>> maps;
818 Iterator new_iter;
819 Call_Id id;
820 ComponentRef name;
821 Type ty;
822 Call_Aux aux;
823 Option<Call_Aux> aux_opt;
824 list<Expression> tpl_lst;
825 algorithm
826 // strip nested iterator for the iterators that actually occure in the function call
827
2/2
✓ Branch 1 taken 51 times.
✓ Branch 2 taken 794 times.
845 if not Iterator.isEmpty(iter) then
828 51 (names, ranges, maps) := Iterator.getFrames(iter);
829 51 new_iter := Iterator.fromFrames(filterFrames(exp, names, ranges, maps));
830 else
831 new_iter := iter;
832 end if;
833
834 // check if call id already exists in the map
835 845 id := CALL_ID(exp, new_iter);
836 845 aux_opt := UnorderedMap.get(id, map);
837
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 845 times.
✓ Branch 2 taken 636 times.
✓ Branch 3 taken 209 times.
845 if isSome(aux_opt) then
838 209 aux := Util.getOption(aux_opt);
839 209 exp := aux.replacer;
840 else
841 // create auxilliary variables for each cat call argument as well (needed for inline)
842 (exp, aux_opt) := match exp
843 local
844 Call call;
845 Expression arg1, arg2;
846
847 case Expression.CALL(call = call as Call.TYPED_CALL()) algorithm
848 () := match (Call.functionName(call), call.arguments)
849 case (Absyn.IDENT(name = "cat"), _) algorithm
850
8/8
✓ Branch 1 taken 304 times.
✓ Branch 2 taken 104 times.
✓ Branch 3 taken 304 times.
✓ Branch 4 taken 104 times.
✓ Branch 6 taken 225 times.
✓ Branch 7 taken 79 times.
✓ Branch 9 taken 178 times.
✓ Branch 10 taken 47 times.
512 call.arguments := listHead(call.arguments) :: list(if Expression.isLiteral(arg) or Expression.isCref(arg) then arg
851 else introduceAlias(arg, map, aux_index, aux_name, iter, init) for arg in listRest(call.arguments));
852 104 exp.call := call;
853 104 id := CALL_ID(exp, new_iter);
854 // double check if after replacing cat() call arguments the ID already exists
855 104 aux_opt := UnorderedMap.get(id, map);
856 then ();
857
858 case (Absyn.IDENT(name = "promote"), {arg1, arg2}) algorithm
859
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 call.arguments := {if Expression.isLiteral(arg1) or Expression.isCref(arg1) then arg1
860 else introduceAlias(arg1, map, aux_index, aux_name, iter, init), arg2};
861 2 exp.call := call;
862 2 id := CALL_ID(exp, new_iter);
863 // double check if after replacing promote() call arguments the ID already exists
864 2 aux_opt := UnorderedMap.get(id, map);
865 then ();
866
867 else ();
868 end match;
869 then (exp, aux_opt);
870 else (exp, aux_opt);
871 end match;
872
873 // for initial systems create parameters, otherwise use type to determine variable kind
874 636 ty := Expression.typeOf(exp);
875 exp := match (aux_opt, ty)
876 30 case (SOME(aux), _) then aux.replacer;
877 case (_, Type.TUPLE()) algorithm
878
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 time.
3 names := list(Call_Aux.createName(sub_ty, new_iter, aux_index, aux_name, init) for sub_ty in ty.types);
879
5/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
3 tpl_lst := list(if ComponentRef.size(cref, true) == 0 then Expression.fromCref(ComponentRef.WILD()) else Expression.fromCref(cref) for cref in names);
880 1 then Expression.TUPLE(ty, tpl_lst);
881 else algorithm
882 605 name := Call_Aux.createName(ty, new_iter, aux_index, aux_name, init);
883 605 then Expression.fromCref(name);
884 end match;
885
886
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 636 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 606 times.
636 if isNone(aux_opt) then
887 // create auxilliary and add to map if there was none before
888
2/2
✓ Branch 1 taken 600 times.
✓ Branch 2 taken 6 times.
1206 aux := CALL_AUX(exp, if Type.isDiscrete(ty) then EquationKind.DISCRETE else EquationKind.CONTINUOUS, false);
889 606 UnorderedMap.add(id, aux, map);
890 end if;
891 end if;
892 end introduceAlias;
893
894 function checkCallReplacement
895 "returns true if the call should be replaced"
896 input Call call;
897 output Boolean b;
898 protected
899 Function fn = Call.typedFunction(call);
900 algorithm
901
6/6
✓ Branch 1 taken 1272 times.
✓ Branch 2 taken 123 times.
✓ Branch 4 taken 832 times.
✓ Branch 5 taken 440 times.
✓ Branch 7 taken 537 times.
✓ Branch 8 taken 295 times.
1395 b := forceReplacement(fn) or not (Function.isSpecialBuiltin(fn) or replaceException(fn));
902 end checkCallReplacement;
903
904 function forceReplacement
905 "returns true if the call has to be force replaced without exception"
906 input Function fn;
907 output Boolean b;
908 algorithm
909 b := match AbsynUtil.pathFirstIdent(Function.nameConsiderBuiltin(fn))
910 case "cat" then true;
911 case "terminal" then true;
912 else false;
913 end match;
914 end forceReplacement;
915
916 function replaceException
917 "returns true if this call should not be replaced"
918 input Function fn;
919 output Boolean b;
920 protected
921 Absyn.Path path;
922 algorithm
923 // do not replace record constructors
924
7/8
✓ Branch 1 taken 832 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 827 times.
✓ Branch 5 taken 5 times.
✓ Branch 7 taken 812 times.
✓ Branch 8 taken 15 times.
✓ Branch 9 taken 6 times.
✓ Branch 10 taken 806 times.
832 if Function.isDefaultRecordConstructor(fn)
925 or Function.isNonDefaultRecordConstructor(fn)
926 // do not replace impure functions
927 or Function.isImpure(fn)
928 // do not replace functions with no output
929 or listEmpty(fn.outputs) then
930 b := true;
931 26 return;
932 end if;
933
2/2
✓ Branch 1 taken 532 times.
✓ Branch 2 taken 274 times.
806 if not Function.isBuiltin(fn) then
934 b := false;
935 else
936 532 path := Function.nameConsiderBuiltin(fn);
937
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 if not AbsynUtil.pathIsIdent(path) then
938 b := false;
939 else
940 b := match AbsynUtil.pathFirstIdent(path)
941 // functions that trigger events, an alias would lose a surrounding noEvent()
942 case "integer" then true;
943 case "floor" then true;
944 case "ceil" then true;
945 case "div" then true;
946 case "mod" then true;
947 case "rem" then true;
948 case "String" then true;
949 case "$OMC$PositiveMax" then true;
950 case "$OMC$inStreamDiv" then true;
951 else false;
952 end match;
953 end if;
954 end if;
955 end replaceException;
956
957 function filterFrames
958 "filters the list of frames for all iterators that occure in exp"
959 input Expression exp;
960 input list<ComponentRef> names;
961 input list<Expression> ranges;
962 input list<Option<Iterator>> maps;
963 output list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
964 protected
965 type FrameTuple = tuple<Expression, Option<Iterator>>;
966 UnorderedMap<ComponentRef, FrameTuple> frame_map = UnorderedMap.new<FrameTuple>(ComponentRef.hash, ComponentRef.isEqual);
967 UnorderedMap<ComponentRef, FrameTuple> new_map = UnorderedMap.new<FrameTuple>(ComponentRef.hash, ComponentRef.isEqual);
968 UnorderedMap<ComponentRef, ComponentRef> sub_map = UnorderedMap.new<ComponentRef>(ComponentRef.hash, ComponentRef.isEqual);
969
970 function collectFrames
971 input output Expression exp;
972 input UnorderedMap<ComponentRef, FrameTuple> frame_map;
973 input UnorderedMap<ComponentRef, FrameTuple> new_map;
974 input UnorderedMap<ComponentRef, ComponentRef> sub_map;
975 algorithm
976 () := match exp
977 local
978 FrameTuple frame_tpl;
979 ComponentRef parent;
980
981 // check if the cref is an iterator
982 case Expression.CREF() algorithm
983 () := match UnorderedMap.get(exp.cref, frame_map)
984 case SOME(frame_tpl) algorithm
985 // add to new map
986 59 UnorderedMap.add(exp.cref, frame_tpl, new_map);
987 then ();
988
989 // if not, check if it has a parent in the sub_map
990 else algorithm
991 () := match UnorderedMap.get(exp.cref, sub_map)
992 // check if the parent is an iterator (should always be)
993 case SOME(parent) algorithm
994 () := match UnorderedMap.get(parent, frame_map)
995 case SOME(frame_tpl) algorithm
996 // add parent to new map
997 ✗ UnorderedMap.add(parent, frame_tpl, new_map);
998 then ();
999 else ();
1000 end match;
1001 then ();
1002 else ();
1003 end match;
1004 then ();
1005 end match;
1006 then ();
1007 else ();
1008 end match;
1009 end collectFrames;
1010
1011 ComponentRef name;
1012 Expression range;
1013 Option<Iterator> map;
1014 list<ComponentRef> n = names, local_n;
1015 list<Expression> r = ranges;
1016 list<Option<Iterator>> m = maps;
1017 algorithm
1018 // iterate over all iterators
1019
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 51 times.
115 while not listEmpty(n) loop
1020 // getting the name, range and map
1021 64 name :: n := n;
1022
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 range :: r := r;
1023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 map :: m := m;
1024 // add the name range and map to the frame_map
1025 64 UnorderedMap.add(name, (range, map), frame_map);
1026 // if there is a sub map add it to the sub_map... mapper
1027
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✓ Branch 2 taken 64 times.
✗ Branch 3 not taken.
64 if isSome(map) then
1028 ✗ (local_n, _, _) := Iterator.getFrames(Util.getOption(map));
1029 ✗ for cref in local_n loop
1030 ✗ UnorderedMap.add(cref, name, sub_map);
1031 end for;
1032 end if;
1033 end while;
1034
1035 51 Expression.map(exp, function collectFrames(frame_map = frame_map, new_map = new_map, sub_map = sub_map));
1036 51 n := UnorderedMap.keyList(new_map);
1037 51 (r, m) := List.unzip(UnorderedMap.valueList(new_map));
1038 51 frames := List.zip3(n, r, m);
1039 end filterFrames;
1040
1041 function addAuxVar
1042 "add the aux var to the correct list and potentially resolve records properly"
1043 input Pointer<Variable> new_var;
1044 input output Boolean disc;
1045 input output list<Pointer<Variable>> new_vars_disc;
1046 input output list<Pointer<Variable>> new_vars_cont;
1047 input output list<Pointer<Variable>> new_vars_init;
1048 input output list<Pointer<Variable>> new_vars_recd;
1049 input Boolean init;
1050 protected
1051 list<Variable> children;
1052 algorithm
1053
2/2
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 676 times.
724 if BVariable.isRecord(new_var) then
1054 48 new_vars_recd := new_var :: new_vars_recd;
1055 // create record element variables (ignore first output since its the variable itself)
1056
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
48 _ :: children := Variable.expandChildren(Pointer.access(new_var), addDimensions = false);
1057
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 48 times.
165 for child in children loop
1058 117 (disc, new_vars_disc, new_vars_cont, new_vars_init, new_vars_recd) := addAuxVar(BVariable.makeVarPtr(child, child.name), disc, new_vars_disc, new_vars_cont, new_vars_init, new_vars_recd, init);
1059 end for;
1060 elseif init then
1061 464 new_vars_init := BVariable.setFixed(new_var, false) :: new_vars_init;
1062 elseif BVariable.isContinuous(new_var, false) then
1063 disc := false;
1064 441 new_vars_cont := new_var :: new_vars_cont;
1065 else
1066 3 new_vars_disc := new_var :: new_vars_disc;
1067 end if;
1068 end addAuxVar;
1069
1070 function addClockedAlias
1071 "add clocked alias equations and variables
1072 Note: inferred clocks are handled as unknowns for partitioning"
1073 input EquationPointers equations;
1074 input Pointer<Integer> eqn_idx;
1075 output list<Pointer<Equation>> clock_eqns = {};
1076 output list<Pointer<Equation>> infer_eqns = {};
1077 output list<Pointer<Variable>> clock_vars;
1078 output list<Pointer<Variable>> infer_vars;
1079 output UnorderedMap<BClock, ComponentRef> clck_coll = UnorderedMap.new<ComponentRef>(BClock.hash, BClock.isEqual);
1080 output UnorderedMap<BClock, ComponentRef> infr_coll = UnorderedMap.new<ComponentRef>(BClock.hash, BClock.isEqual);
1081 protected
1082 Pointer<list<Pointer<Variable>>> new_clocks = Pointer.create({});
1083 Pointer<list<Pointer<Variable>>> new_infers = Pointer.create({});
1084 Pointer<Integer> idx = Pointer.create(0);
1085 BClock clock;
1086 ComponentRef clock_name;
1087 algorithm
1088 193 EquationPointers.map(equations, function Partitioning.extractClocksEqn(
1089 clck_coll = clck_coll, infr_coll = infr_coll, new_clocks = new_clocks, new_infers = new_infers, idx = idx));
1090
1091 // create clocks
1092 193 clock_vars := Pointer.access(new_clocks);
1093
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 193 times.
195 for tpl in UnorderedMap.toList(clck_coll) loop
1094 2 (clock, clock_name) := tpl;
1095 2 clock_eqns := Equation.makeAssignment(Expression.fromCref(clock_name), BClock.toExp(clock), eqn_idx, "AUX", Iterator.EMPTY(), EquationAttributes.default(EquationKind.CLOCKED, false)) :: clock_eqns;
1096 end for;
1097
1098 // create inferred clocks
1099 193 infer_vars := Pointer.access(new_infers);
1100
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 193 times.
193 for tpl in UnorderedMap.toList(infr_coll) loop
1101 ✗ (clock, clock_name) := tpl;
1102 ✗ infer_eqns := Equation.makeAssignment(Expression.fromCref(clock_name), BClock.toExp(clock), eqn_idx, "AUX", Iterator.EMPTY(), EquationAttributes.default(EquationKind.CLOCKED, false)) :: infer_eqns;
1103 end for;
1104 end addClockedAlias;
1105
1106 // type for slice collection
1107 type Indices = UnorderedSet<Integer>;
1108
1109 function collectSlicedStatesAliasEquation
1110 "helper function to map equations for sliced state collection"
1111 input output Equation eqn;
1112 input UnorderedMap<ComponentRef,Indices> map;
1113 protected
1114 Iterator iter = Equation.getForIterator(eqn);
1115 algorithm
1116 2934 Equation.map(eqn, function collectSlicedStatesAlias(iter = iter, map = map), NONE(), Expression.fakeMap);
1117 end collectSlicedStatesAliasEquation;
1118
1119 function collectSlicedStatesAlias
1120 "check cref in a der() call for full access of the variable.
1121 if its not fully accessing the variable, add it to the slice map."
1122 input output Expression exp;
1123 input Iterator iter;
1124 input UnorderedMap<ComponentRef,Indices> map;
1125 algorithm
1126 exp := match exp
1127 local
1128 Integer iter_size, cref_size, var_size;
1129 Expression arg;
1130 UnorderedSet<ComponentRef> call_crefs;
1131 ComponentRef stripped_cref;
1132 UnorderedSet<Integer> indices;
1133 list<ComponentRef> names;
1134 list<Expression> ranges;
1135 list<Option<Iterator>> maps;
1136
1137 // derivative
1138 case Expression.CALL(call = Call.TYPED_CALL(fn = Function.FUNCTION(path = Absyn.IDENT(name = "der")), arguments = {arg})) algorithm
1139 // get the iterator size
1140 185 iter_size := Iterator.size(iter, true);
1141 // collect all crefs in the call
1142 185 call_crefs := UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
1143 185 Slice.filterExp(arg, function Slice.getContinuous(init = false), call_crefs);
1144
2/2
✓ Branch 1 taken 183 times.
✓ Branch 2 taken 185 times.
368 for cref in UnorderedSet.toList(call_crefs) loop
1145 183 cref_size := Type.sizeOf(ComponentRef.getSubscriptedType(cref), true);
1146 183 var_size := BVariable.size(BVariable.getVarPointer(cref, sourceInfo()), true);
1147 // if the cref does not represent the full variable it has to be collected as it might cause a sliced state
1148
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 151 times.
183 if var_size <> cref_size * iter_size then
1149 32 stripped_cref := ComponentRef.stripSubscriptsAll(cref);
1150 32 indices := UnorderedMap.getOrDefault(stripped_cref, map, UnorderedSet.new(Util.id, intEq));
1151 32 (names, ranges, maps) := Iterator.getFrames(iter);
1152 // get all the local indices (start index = 0) and collect with potential previous indices
1153
2/2
✓ Branch 2 taken 146 times.
✓ Branch 3 taken 32 times.
178 for index in Slice.getCrefInFrameIndicesLocal(cref, stripped_cref, List.zip3(names, ranges, maps), 0, true) loop
1154 146 UnorderedSet.add(index, indices);
1155 end for;
1156 32 UnorderedMap.add(stripped_cref, indices, map);
1157 end if;
1158 end for;
1159 then exp;
1160
1161 // array constructor, get the iterators and add them going deeper
1162 case Expression.CALL(call = Call.TYPED_ARRAY_CONSTRUCTOR()) algorithm
1163 15 Expression.mapShallow(exp, function collectSlicedStatesAlias(iter = Iterator.expand(iter, exp.call), map = map));
1164 then exp;
1165 case Expression.CALL(call = Call.TYPED_REDUCTION()) algorithm
1166 8 Expression.mapShallow(exp, function collectSlicedStatesAlias(iter = Iterator.expand(iter, exp.call), map = map));
1167 then exp;
1168
1169 // just map deeper in search for der() calls
1170 else algorithm
1171 20886 Expression.mapShallow(exp, function collectSlicedStatesAlias(iter = iter, map = map));
1172 then exp;
1173 end match;
1174 end collectSlicedStatesAlias;
1175
1176 function getSlicedStatesSet
1177 "takes the map of states and their sliced indices and returns the set of all states of which the indices are not covering the full state"
1178 input UnorderedMap<ComponentRef,Indices> map;
1179 output UnorderedSet<ComponentRef> set = UnorderedSet.new(ComponentRef.hash, ComponentRef.isEqual);
1180 protected
1181 ComponentRef state;
1182 UnorderedSet<Integer> indices;
1183 algorithm
1184
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 190 times.
198 for tpl in UnorderedMap.toList(map) loop
1185 8 (state, indices) := tpl;
1186
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 if BVariable.size(BVariable.getVarPointer(state, sourceInfo()), true) <> UnorderedSet.size(indices) then
1187 ✗ UnorderedSet.add(state, set);
1188 end if;
1189 end for;
1190 end getSlicedStatesSet;
1191
1192 function introduceSlicedStateAliasEquation
1193 "creates auxilliary variables for all state slices where the variable is only partially a state"
1194 input output Equation eqn;
1195 input UnorderedSet<ComponentRef> set;
1196 input UnorderedMap<Call_Id, Call_Aux> map;
1197 input Pointer<Integer> aux_index;
1198 protected
1199 Iterator iter = Equation.getForIterator(eqn);
1200 algorithm
1201 ✗ eqn := Equation.map(eqn, function introduceSlicedStateAliasExp(set = set, map = map, iter = iter, aux_index = aux_index), NONE(), Expression.fakeMap);
1202 end introduceSlicedStateAliasEquation;
1203
1204 function introduceSlicedStateAliasExp
1205 "replace component references in der() calls that have been found to be only partially states."
1206 input output Expression exp;
1207 input UnorderedSet<ComponentRef> set;
1208 input UnorderedMap<Call_Id, Call_Aux> map;
1209 input Iterator iter;
1210 input Pointer<Integer> aux_index;
1211 algorithm
1212 exp := match exp
1213 local
1214 Call call;
1215 Expression arg, new_exp;
1216
1217 case Expression.CALL(call = call as Call.TYPED_CALL(fn = Function.FUNCTION(path = Absyn.IDENT(name = "der")), arguments = {arg})) algorithm
1218 ✗ call.arguments := {Expression.map(arg, function introduceAliasCrefConditional(set = set, map = map, iter = iter, aux_index = aux_index, init = false))};
1219 ✗ exp.call := call;
1220 then exp;
1221
1222 // array constructor, get the iterators and add them going deeper
1223 case Expression.CALL(call = Call.TYPED_ARRAY_CONSTRUCTOR()) algorithm
1224 ✗ new_exp := Expression.mapShallow(exp, function introduceSlicedStateAliasExp(set = set, map = map, iter = Iterator.expand(iter, exp.call), aux_index = aux_index));
1225 then new_exp;
1226 case Expression.CALL(call = Call.TYPED_REDUCTION()) algorithm
1227 ✗ new_exp := Expression.mapShallow(exp, function introduceSlicedStateAliasExp(set = set, map = map, iter = Iterator.expand(iter, exp.call), aux_index = aux_index));
1228 then new_exp;
1229
1230 // just map deeper in search for der() calls
1231 else algorithm
1232 ✗ new_exp := Expression.mapShallow(exp, function introduceSlicedStateAliasExp(set = set, map = map, iter = iter, aux_index = aux_index));
1233 then new_exp;
1234 end match;
1235 end introduceSlicedStateAliasExp;
1236
1237 annotation(__OpenModelica_Interface="nbackend");
1238 end NBFunctionAlias;
1239