Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 87.0% 60 / 0 / 69
Functions: -% 0 / 1 / 1
Branches: 84.0% 42 / 0 / 50

OMCompiler/Compiler/NFFrontEnd/NFFunctionDerivative.mo
Line Branch Exec Source
1 /*
2 * This file is part of OpenModelica.
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC),
5 * c/o Linköpings universitet, Department of Computer and Information Science,
6 * SE-58183 Linköping, Sweden.
7 *
8 * All rights reserved.
9 *
10 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR
11 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8.
12 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
13 * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL
14 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
15 *
16 * The OpenModelica software and the OSMC (Open Source Modelica Consortium)
17 * Public License (OSMC-PL) are obtained from OSMC, either from the above
18 * address, from the URLs:
19 * http://www.openmodelica.org or
20 * https://github.com/OpenModelica/ or
21 * http://www.ida.liu.se/projects/OpenModelica,
22 * and in the OpenModelica distribution.
23 *
24 * GNU AGPL version 3 is obtained from:
25 * https://www.gnu.org/licenses/licenses.html#GPL
26 *
27 * This program is distributed WITHOUT ANY WARRANTY; without
28 * even the implied warranty of MERCHANTABILITY or FITNESS
29 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
30 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
31 *
32 * See the full OSMC Public License conditions for more details.
33 *
34 */
35
36 encapsulated uniontype NFFunctionDerivative
37 import Absyn;
38 import AbsynUtil;
39 import SCode;
40 import NFInstNode.InstNode;
41 import NFInstNode;
42 import NFFunction.Function;
43 import Expression = NFExpression;
44 import Type = NFType;
45 import Util;
46
47 protected
48 import SCodeDump;
49 import SCodeUtil;
50 import Inst = NFInst;
51 import Typing = NFTyping;
52 import TypeCheck = NFTypeCheck;
53 import MatchKind = NFTypeCheck.MatchKind;
54 import Ceval = NFCeval;
55 import DAE;
56 import EvalTarget = NFCeval.EvalTarget;
57 import Prefixes = NFPrefixes;
58 import NFPrefixes.Variability;
59 import InstContext = NFInstContext;
60
61 import FunctionDerivative = NFFunctionDerivative;
62
63 public
64 type Condition = enumeration(ZERO_DERIVATIVE, NO_DERIVATIVE);
65
66 record FUNCTION_DER
67 NFInstNode.ScopeRef derivativeFn "Weakly: the class tree owns the function
68 nodes; their caches hold the functions that name these back.";
69 NFInstNode.ScopeRef derivedFn;
70 Expression order "Is evaluated to a literal Integer during typing";
71 list<tuple<Integer, String, Condition>> conditions;
72 list<NFInstNode.ScopeRef> lowerOrderDerivatives;
73 end FUNCTION_DER;
74
75 function instDerivatives
76 input InstNode fnNode;
77 input Function fn;
78 output list<FunctionDerivative> ders = {};
79 protected
80 list<SCode.Mod> der_mods;
81 InstNode scope;
82 algorithm
83 23760 der_mods := getDerivativeAnnotations(InstNode.definition(fnNode));
84 23760 scope := InstNode.parent(fnNode);
85
86
2/2
✓ Branch 0 taken 952 times.
✓ Branch 1 taken 23760 times.
24712 for m in der_mods loop
87 952 ders := instDerivativeMod(m, fnNode, fn, scope, ders);
88 end for;
89 end instDerivatives;
90
91 function typeDerivative
92 input FunctionDerivative fnDer;
93 protected
94 MatchKind mk;
95 Expression order;
96 Type order_ty;
97 Variability var;
98 SourceInfo info;
99 algorithm
100 893 Function.typeNodeCache(InstNode.borrow(fnDer.derivativeFn));
101 893 info := InstNode.info(InstNode.borrow(fnDer.derivedFn));
102
103 893 (order, order_ty, var) := Typing.typeExp(fnDer.order, NFInstContext.FUNCTION, info);
104 893 (order, _, mk) := TypeCheck.matchTypes(order_ty, Type.INTEGER(), order);
105
106
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 892 times.
893 if TypeCheck.isIncompatibleMatch(mk) then
107 3 Error.addSourceMessage(Error.VARIABLE_BINDING_TYPE_MISMATCH,
108 {"order", Expression.toString(order), "Integer", Type.toString(order_ty)}, info);
109 1 fail();
110 end if;
111
112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 892 times.
892 if var > Variability.CONSTANT then
113 ✗ Error.addSourceMessage(Error.HIGHER_VARIABILITY_BINDING,
114 {"order", Prefixes.variabilityString(Variability.CONSTANT),
115 Expression.toString(order), Prefixes.variabilityString(var)}, info);
116 ✗ fail();
117 end if;
118
119 892 order := Ceval.evalExp(order, EvalTarget.new(info));
120 end typeDerivative;
121
122 function toDAE
123 input FunctionDerivative fnDer;
124 output DAE.FunctionDefinition derDef;
125 protected
126 Integer order;
127 algorithm
128
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 783 times.
783 Expression.INTEGER(order) := fnDer.order;
129
130
8/8
✓ Branch 0 taken 497 times.
✓ Branch 1 taken 783 times.
✓ Branch 2 taken 497 times.
✓ Branch 3 taken 783 times.
✓ Branch 5 taken 41 times.
✓ Branch 6 taken 783 times.
✓ Branch 7 taken 41 times.
✓ Branch 8 taken 783 times.
1321 derDef := DAE.FunctionDefinition.FUNCTION_DER_MAPPER(
131 Function.name(listHead(Function.getCachedFuncs(InstNode.borrow(fnDer.derivedFn)))),
132 Function.name(listHead(Function.getCachedFuncs(InstNode.borrow(fnDer.derivativeFn)))),
133 order,
134 list(conditionToDAE(c) for c in fnDer.conditions),
135 // TODO: Figure out if the two fields below are needed.
136 NONE(),
137 list(Function.name(listHead(Function.getCachedFuncs(InstNode.borrow(fn)))) for fn in fnDer.lowerOrderDerivatives)
138 );
139 end toDAE;
140
141 function conditionToDAE
142 input tuple<Integer, String, Condition> cond;
143 output tuple<Integer, DAE.derivativeCond> daeCond;
144 protected
145 Integer idx;
146 Condition c;
147 algorithm
148 497 (idx, _, c) := cond;
149
150 daeCond := match c
151 case Condition.ZERO_DERIVATIVE
152 82 then (idx, DAE.derivativeCond.ZERO_DERIVATIVE());
153 // TODO: DAE.NO_DERIVATIVE contains an expression for historical reasons,
154 // but this was changed in Modelica 3.2 rev2 and should be removed
155 // from the DAE (it doesn't seem to have ever been used anyway).
156 case Condition.NO_DERIVATIVE
157 415 then (idx, DAE.derivativeCond.NO_DERIVATIVE(DAE.Exp.ICONST(99)));
158 end match;
159 end conditionToDAE;
160
161 function toSubMod
162 input FunctionDerivative fnDer;
163 output SCode.SubMod subMod;
164 protected
165 tuple<Integer,Condition> tpl;
166 Condition condition;
167 String id;
168 SCode.Mod mod;
169 SCode.SubMod orderMod;
170 list<SCode.SubMod> subMods;
171 Integer order;
172 SourceInfo info;
173 Function func;
174 algorithm
175 2 info := InstNode.info(InstNode.borrow(fnDer.derivedFn));
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 Expression.INTEGER(order) := fnDer.order;
177 4 orderMod := SCode.NAMEMOD("order", SCode.MOD(SCode.NOT_FINAL(), SCode.NOT_EACH(), {}, SOME(Absyn.INTEGER(order)), NONE(), info));
178
179 subMods := {};
180
181
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for tpl in fnDer.conditions loop
182 2 (_, id, condition) := tpl;
183 4 subMods := SCode.NAMEMOD(conditionToString(condition), SCode.MOD(SCode.NOT_FINAL(), SCode.NOT_EACH(), {}, SOME(Absyn.CREF(Absyn.CREF_IDENT(id, {}))), NONE(), info)) :: subMods;
184 end for;
185
186
187 2 func := listHead(Function.getCachedFuncs(InstNode.borrow(fnDer.derivativeFn)));
188
189 4 mod := SCode.MOD(SCode.NOT_FINAL(), SCode.NOT_EACH(), orderMod::subMods,
190 SOME(Absyn.CREF(Absyn.CREF_IDENT(AbsynUtil.pathString(func.path),{}))), NONE(), info);
191 2 subMod := SCode.NAMEMOD("derivative", mod);
192 end toSubMod;
193
194 function perfectFit
195 "checks if the derivative is a perfect fit for specified interface map"
196 input FunctionDerivative fnDer;
197 input UnorderedMap<String, Boolean> interface_map;
198 output Boolean b = true;
199 protected
200 String name;
201 Condition cond;
202 algorithm
203
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 46 times.
74 for condition in fnDer.conditions loop
204 28 (_, name, cond) := condition;
205 // if a zero derivative is required but the argument is not in the map
206 // this function derivative cannot be used
207
4/4
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 25 times.
28 if cond == Condition.ZERO_DERIVATIVE and not UnorderedMap.contains(name, interface_map) then
208 b := false;
209 1 return;
210 end if;
211 end for;
212 // the function derivative is a perfect fit, add all conditions to the interface
213
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 46 times.
73 for condition in fnDer.conditions loop
214 27 (_, name, _) := condition;
215 27 UnorderedMap.add(name, true, interface_map);
216 end for;
217 end perfectFit;
218
219 function conditionsFromMap
220 input UnorderedMap<String, Boolean> interface_map;
221 output list<tuple<Integer, String, Condition>> conditions = {};
222 protected
223 String name;
224 Boolean isZeroDer;
225 algorithm
226
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 48 times.
82 for tpl in UnorderedMap.toList(interface_map) loop
227 34 (name, isZeroDer) := tpl;
228
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 if isZeroDer then
229 // ToDo: what siginificance does the integer have?
230 34 conditions := (0, name, Condition.ZERO_DERIVATIVE) :: conditions;
231 end if;
232 end for;
233 end conditionsFromMap;
234
235 protected
236
237 function conditionToString
238 input Condition condition;
239 output String str;
240 algorithm
241 str := match condition
242 case Condition.NO_DERIVATIVE then "noDerivative";
243 case Condition.ZERO_DERIVATIVE then "zeroDerivative";
244 ✗ else String(condition);
245 end match;
246 end conditionToString;
247
248 function getDerivativeAnnotations
249 input SCode.Element definition;
250 output list<SCode.Mod> derMods;
251 algorithm
252 derMods := match definition
253 local
254 SCode.Annotation ann;
255
256 case SCode.Element.CLASS(cmt = SCode.Comment.COMMENT(annotation_ = SOME(ann)))
257 20444 then SCodeUtil.lookupAnnotations(ann, "derivative");
258
259 else {};
260 end match;
261 end getDerivativeAnnotations;
262
263 function instDerivativeMod
264 input SCode.Mod mod;
265 input InstNode fnNode;
266 input Function fn;
267 input InstNode scope;
268 input output list<FunctionDerivative> fnDers;
269 algorithm
270 fnDers := match mod
271 local
272 list<SCode.SubMod> attrs;
273 Absyn.ComponentRef acref;
274 InstNode der_node;
275 Expression order;
276 list<tuple<Integer, String, Condition>> conds;
277
278 case SCode.Mod.MOD(subModLst = attrs, binding = SOME(Absyn.CREF(acref)))
279 algorithm
280 951 (_, der_node) := Function.instFunction(acref, scope, NFInstContext.NO_CONTEXT, mod.info);
281 951 addLowerOrderDerivative(der_node, fnNode);
282 951 (order, conds) := getDerivativeAttributes(attrs, fn, fnNode, mod.info);
283 951 then
284 FUNCTION_DER(InstNode.identityCell(der_node), InstNode.identityCell(fnNode), order, conds, {}) :: fnDers;
285
286 // Give a warning if the derivative annotation doesn't specify a function name.
287 case SCode.Mod.MOD()
288 algorithm
289 2 Error.addStrictMessage(Error.MISSING_FUNCTION_DERIVATIVE_NAME,
290 {AbsynUtil.pathString(Function.name(fn))}, mod.info);
291 then
292 fnDers;
293
294 // We shouldn't get any NOMODs here since they're filtered out when
295 // translating Absyn to SCode, and redeclare isn't allowed by the syntax.
296 else
297 algorithm
298 ✗ Error.terminate(getInstanceName() + " got invalid modifier", sourceInfo());
299 ✗ then
300 fail();
301
302 end match;
303 end instDerivativeMod;
304
305 function getDerivativeAttributes
306 input list<SCode.SubMod> attrs;
307 input Function fn;
308 input InstNode scope;
309 input SourceInfo info;
310 output Expression order = Expression.EMPTY(Type.UNKNOWN());
311 output list<tuple<Integer, String, Condition>> conditions = {};
312 protected
313 String id;
314 SCode.Mod mod;
315 Absyn.Exp aexp;
316 Integer index;
317 algorithm
318
2/2
✓ Branch 0 taken 759 times.
✓ Branch 1 taken 951 times.
1710 for attr in attrs loop
319 759 SCode.SubMod.NAMEMOD(id, mod) := attr;
320
321 () := match (id, mod)
322 case ("order", SCode.Mod.MOD(binding = SOME(aexp)))
323 algorithm
324
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 66 times.
66 if not Expression.isEmpty(order) then
325 ✗ Error.addSourceMessage(Error.DUPLICATE_MODIFICATIONS,
326 {id, "derivative"}, info);
327 end if;
328
329 66 order := Inst.instExp(aexp, scope, NFInstContext.NO_CONTEXT, info);
330 then
331 ();
332
333 case ("noDerivative", SCode.Mod.MOD(binding = SOME(Absyn.CREF(componentRef = Absyn.CREF_IDENT(name = id)))))
334 algorithm
335 589 index := getInputIndex(id, fn, info);
336 589 conditions := (index, id, Condition.NO_DERIVATIVE) :: conditions;
337 then
338 ();
339
340 case ("zeroDerivative", SCode.Mod.MOD(binding = SOME(Absyn.CREF(componentRef = Absyn.CREF_IDENT(name = id)))))
341 algorithm
342 104 index := getInputIndex(id, fn, info);
343 104 conditions := (index, id, Condition.ZERO_DERIVATIVE) :: conditions;
344 then
345 ();
346
347 else
348 algorithm
349 ✗ Error.addStrictMessage(Error.INVALID_FUNCTION_ANNOTATION_ATTR,
350 {id + (if SCodeUtil.isEmptyMod(mod) then "" else " = " + SCodeDump.printModStr(mod)), "derivative"}, info);
351 then
352 ();
353
354 end match;
355 end for;
356
357
2/2
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 885 times.
951 if Expression.isEmpty(order) then
358 order := Expression.INTEGER(1);
359 end if;
360 end getDerivativeAttributes;
361
362 function getInputIndex
363 input String name;
364 input Function fn;
365 input SourceInfo info;
366 output Integer index = 1;
367 algorithm
368
1/2
✓ Branch 0 taken 1873 times.
✗ Branch 1 not taken.
1873 for i in fn.inputs loop
369
4/4
✓ Branch 1 taken 889 times.
✓ Branch 2 taken 984 times.
✓ Branch 5 taken 693 times.
✓ Branch 6 taken 196 times.
1873 if InstNode.name(i) == name then
370 693 return;
371 end if;
372
373 1180 index := index + 1;
374 end for;
375
376 ✗ Error.addSourceMessage(Error.INVALID_FUNCTION_ANNOTATION_INPUT,
377 {name, AbsynUtil.pathString(Function.name(fn))}, info);
378 ✗ fail();
379 end getInputIndex;
380
381 function addLowerOrderDerivative
382 input InstNode fnNode;
383 input InstNode lowerDerNode;
384 algorithm
385 951 Function.mapCachedFuncs(fnNode, function addLowerOrderDerivative2(lowerDerNode = lowerDerNode));
386 end addLowerOrderDerivative;
387
388 function addLowerOrderDerivative2
389 input output Function fn;
390 input InstNode lowerDerNode;
391 algorithm
392
4/4
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 951 times.
✓ Branch 2 taken 57 times.
✓ Branch 3 taken 951 times.
1008 fn.derivatives := list(
393 match fn_der
394 case FUNCTION_DER()
395 algorithm
396 114 fn_der.lowerOrderDerivatives := InstNode.identityCell(lowerDerNode) :: fn_der.lowerOrderDerivatives;
397 then
398 fn_der;
399 end match
400 for fn_der in fn.derivatives);
401 end addLowerOrderDerivative2;
402
403 annotation(__OpenModelica_Interface="nf_frontend");
404 end NFFunctionDerivative;
405
406