Opened 10 years ago

Closed 10 years ago

#3053 closed defect (fixed)

Unimplemented derivatives for built-in Modelica operators

Reported by: Rüdiger Franke Owned by: Willi Braun
Priority: high Milestone: 1.9.2
Component: FMI Version: trunk
Keywords: Cc:

Description

Thank you for prompt fix to #3048. FMU export works now for my models that use max! Are there more unimplemented derivatives?

translateModelFMU(Rem, version="2.0") for the model:

model Rem
  input Real x;
  input Real y;
  output Real r1, r2;
equation
  r1 = rem(x,y);
  r2 = x - div(x,y)*y;
end Rem;

results in

Error: Internal error BackendDAEOptimize.generateSymbolicJacobian failed
Error: Internal error BackendDAEOptimize.createJacobian failed

Once the reason is known, the use of wrapper functions seems to provide a workaround:

function myrem
  input Real x, y;
  output Real r;
algorithm
  r := rem(x,y);
end myrem;

function mydiv
  input Real x, y;
  output Real d;
algorithm
  d := div(x,y);
end mydiv;

model Rem
  input Real x;
  input Real y;
  output Real r1, r2;
equation
  r1 = myrem(x,y);
  r2 = x - mydiv(x,y)*y;
end Rem;

Change History (12)

comment:1 by Willi Braun, 10 years ago

Owner: changed from Adeel Asghar to Willi Braun
Status: newassigned

comment:2 by Rüdiger Franke, 10 years ago

Priority: normalhigh

The error message does not show up anymore in r24671. Instead the dependency is just ignored in the ModelStructure of an exported FMU 2. This is of course worse than an error message.

Shouldn't Differentiate.differentiateCallExp1Arg and Differentiate.differentiateCallExpNArg at least have some else branch that raises an error -- or even better forward the differentiation to some default implementation?

Note: the above workaround generates the right ModelStructure. The calls to rem and div are wrapped into functions -- couldn't the else branch behave as if unknown calls were such user functions?

comment:3 by Rüdiger Franke, 10 years ago

Just checked out from svn and built omc myself. The error message was back for translateModelFMU(Rem, version="2.0"). Can it be that OMEdit ignores error messages?

Anyway, I added two more rules to Differentiate.differentiateCallExpNArg, attempting to assume that the derivatives of div and rem are the same as for /. See svn diff:

user@utopic:~/OpenModelica$ svn diff
Index: Compiler/BackEnd/Differentiate.mo
===================================================================
--- Compiler/BackEnd/Differentiate.mo	(revision 24752)
+++ Compiler/BackEnd/Differentiate.mo	(working copy)
@@ -1685,6 +1685,14 @@
       then
         (DAE.IFEXP(DAE.CALL(Absyn.IDENT("noEvent"),{DAE.RELATION(e1,DAE.LESS(tp),e2,-1,NONE())},DAE.callAttrBuiltinBool), res1, res2), funcs);
 
+    case ("div", {e1,e2}, DAE.CALL_ATTR(ty=tp), _, _, _, _)
+      then
+        differentiateBinary(DAE.BINARY(e1, DAE.DIV(tp), e2), inDiffwrtCref, inInputData, inDiffType, inFunctionTree);
+
+    case ("rem", {e1,e2}, DAE.CALL_ATTR(ty=tp), _, _, _, _)
+      then
+        differentiateBinary(DAE.BINARY(e1, DAE.DIV(tp), e2), inDiffwrtCref, inInputData, inDiffType, inFunctionTree);
+
   end match;
 end differentiateCallExpNArg;

This makes the FMU generation work and the generated ModelStructure is correct.

Lacking pull requests and not really knowing what I did here, can someone check if this solves the ticket?

Version 0, edited 10 years ago by Rüdiger Franke (next)

comment:4 by Martin Sjölund, 10 years ago

I doubt it solves anything. div and rem are not continuous-time expressions (they trigger events when they change). So the derivative of them should be 0 if anything, right?

comment:5 by Adrian Pop, 10 years ago

Definitely the case for "rem" is wrong, it should be something like e1 - (div(e1, e2) * e2);.

comment:6 by Rüdiger Franke, 10 years ago

Zero is right for div as it only changes at event time points. Then the existing implementation of pre appears wrong though (function differentiateCalls in file Differentiate.mo, line 1333ff):

    case (e as DAE.CALL(path=Absyn.IDENT(name = "pre")), _, _, _, _)
      then
        (e,  inFunctionTree);

(probably another ticket)

So should the implementation for event triggering mathematical functions look something like:

  • Compiler/BackEnd/Differentiate.mo

     
    15731573        (exp_2, funcs) = differentiateExp(exp_1, inDiffwrtCref, inInputData, inDiffType, inFuncs);
    15741574      then
    15751575       (exp_2, funcs);
     1576
     1577    // diff(ceil(exp)) = 0 because changes trigger events
     1578    case ("ceil", _, _, _, _, _)
     1579      then
     1580        (DAE.RCONST(0.0), inFuncs);
     1581
     1582    // diff(floor(exp)) = 0 because changes trigger events
     1583    case ("floor", _, _, _, _, _)
     1584      then
     1585        (DAE.RCONST(0.0), inFuncs);
     1586
     1587    // diff(integer(exp)) = 0 because changes trigger events
     1588    case ("integer", _, _, _, _, _)
     1589      then
     1590        (DAE.RCONST(0.0), inFuncs);
    15761591  end match;
    15771592end differentiateCallExp1Arg;
    15781593
     
    16851700      then
    16861701        (DAE.IFEXP(DAE.CALL(Absyn.IDENT("noEvent"),{DAE.RELATION(e1,DAE.LESS(tp),e2,-1,NONE())},DAE.callAttrBuiltinBool), res1, res2), funcs);
    16871702
     1703    // diff(div(e1, e2)) = 0 because changes trigger events
     1704    case ("div", {e1, e2}, DAE.CALL_ATTR(ty = tp), _, _, _, _)
     1705      then
     1706        (DAE.RCONST(0.0), inFunctionTree);
     1707
     1708    // diff(mod(e1, e2)) = diff(e1 - floor(e1 / e2) * e2)
     1709    case ("mod", {e1, e2}, DAE.CALL_ATTR(ty = tp), _, _, _, _)
     1710      then
     1711        differentiateExp(DAE.BINARY(e1, DAE.SUB(tp), DAE.BINARY(DAE.CALL(Absyn.IDENT("floor"), {e1, e2}, inAttr), DAE.MUL(tp), e2)), inDiffwrtCref, inInputData, inDiffType, inFunctionTree);
     1712
     1713    // diff(rem(e1, e2)) = diff(e1 - div(e1, e2) * e2)
     1714    case ("rem", {e1, e2}, DAE.CALL_ATTR(ty = tp), _, _, _, _)
     1715      then
     1716        differentiateExp(DAE.BINARY(e1, DAE.SUB(tp), DAE.BINARY(DAE.CALL(Absyn.IDENT("div"), {e1, e2}, inAttr), DAE.MUL(tp), e2)), inDiffwrtCref, inInputData, inDiffType, inFunctionTree);
     1717
    16881718  end match;
    16891719end differentiateCallExpNArg;

comment:7 by Rüdiger Franke, 10 years ago

Or do event triggering functions probably need two implementations: one for continuous integration -- zero for div -- and another one for event updates -- division for div?

comment:8 by Martin Sjölund, 10 years ago

der(noEvent(div())) needs an implementation separate from der(div()) in my opinion.

comment:9 by Rüdiger Franke, 10 years ago

I'm having the use case of connected FMUs with algebraic loops in mind. This was a major motivation for introducing ModelStructure and directional derivatives in FMI 2.0.

Shouldn't this also work during event update?

For now it would be great if event triggering functions would pass FMI export and result in the correct ModelStructure.

comment:10 by Rüdiger Franke, 10 years ago

Do the DAE.CALL's possibly have an attribute saying if in event mode or not?

Then the implementation could look like:

    case ("div", {e1, e2}, DAE.CALL_ATTR(ty = tp), _, _, _, _)
      then
        (if inEventUpdate then 
           differentiateBinary(DAE.BINARY(e1, DAE.DIV(tp), e2), inDiffwrtCref, inInputData, inDiffType, inFunctionTree)
         else
           DAE.RCONST(0.0), inFunctionTree);

comment:11 by Martin Sjölund, 10 years ago

That would be making things too easy for implementations ;)

comment:12 by Willi Braun, 10 years ago

Resolution: fixed
Status: assignedclosed

Added changes in r24768 and fixed.

Replying to rfranke:

Shouldn't this also work during event update?

Yes, and it does but the events are triggered
by the functions (div, mod, e.g) and than the
originally value changes, but the derivative
is still zero also at the event, so we don't
need to take care therefore at this point.

Note: See TracTickets for help on using tickets.