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 |