ModelicaSpecification
ModelicaSpecification copied to clipboard
Annotation noDerivative belongs to function being differentiated
It occurs wrong to me that the noDerivative
annotation is attached to the derivative function rather than the function to which the derivative belongs.
To illustrate, I believe this is currently valid:
model NoDerivativeExample
function f
input Real x;
output Real y = if x > 0.0 then 4.0 else 5.0;
annotation(
derivative(noDerivative = x) = f_der1,
derivative = f_der2, /* Derivative is completely "shadowed" by f_der1, but that's not the point here. */
InlineAfterIndexReduction = true
);
end f;
function f_der1
input Real x;
output Real y_der = 0;
end f_der1;
function f_der2
input Real x;
input Real x_der;
output Real y_der = 0;
end f_der2;
Real x;
equation
f(x) = 0.0;
annotation(experiment(StopTime = 1.0));
end NoDerivativeExample;
For symbolic analysis of the model, I'm interested in knowing that f(x)
can't be solved for x
, and I suppose I can tell this by looking for noDerivative = x
in any of the derivative
annotations provided for the function. Thus, it seems wrong that this information is attached to the derivative rather than the entire function. This is what I'd like it to look like:
function f
input Real x;
output Real y = if x > 0.0 then 4.0 else 5.0;
annotation(
noDerivative = x,
derivative = f_der2,
InlineAfterIndexReduction = true
);
end f;
Am I missing something, or could we at least open up for allowing this use of noDerivative
in addition to the current, misplaced, design?
Proposed semantics:
A function input component can have the annotation
noDerivative = true
. When any function input component hasnoDerivative = true
, thederivative
annotations for the same function are not allowed to specify their ownnoDerivative
; instead, the input componentnoDerivative
annotations are in effect for everyderivative
annotation.
Of course, an alternative design would be to allow noDerivative = x
as a top level function annotation, instead of having noDerivative = true
for the input component x
.
It occurs wrong to me that the noDerivative annotation is attached to the derivative function rather than the function to which the derivative belongs.
To me noDerivative is a sort of hack, and I don't think we should change it as the question of using noDerivative in the indicated way shouldn't really work.
In an ideal world it shouldn't specify more and be a semantic restriction (how is unclear). In one sense the function does something even when the semantic restriction is not satisfied, but the derivative does not make sense. In another sense calling the function without satisfying the semantic restriction may be nonsense.
As an example consider Modelica.Media.Air.ReferenceAir.Air_Utilities.h_props_dT which has
derivative(noDerivative=aux) = h_dT_der
The reason we shouldn't differentiate aux
in h_props_dT
is that it should be called as follows:
function h_dT "Specific enthalpy as function of density and temperature"
extends Modelica.Icons.Function;
input SI.Density d "Density";
input SI.Temperature T "Temperature";
output SI.SpecificEnthalpy h "Specific enthalpy";
algorithm
h := h_props_dT(
d,
T,
Air_Utilities.airBaseProp_dT(d, T));
end h_dT;
So ideally we might replace noDerivative=aux
by stating that aux=airBaseProp_dT(d, T)
and forbid other calls of h_props_dT
; but it is technically complicated.
But based on this I believe the question for this case: "can h_props_dT be solved for aux" doesn't really make sense.
First, if noDerivative = x
as used in the example shouldn't really work, aren't we missing a way of annotating a function so that it is clear from the outside that the derivatives will not depend on x
in a differentiable manner? If there was such an annotation, would its semantics have any practical difference compared to the current noDerivative
?
Second, even for the current noDerivative
semantics, wouldn't it make more sense to associate this with the function as a whole rather than with a particular derivative
annotation?
First, if
noDerivative = x
as used in the example shouldn't really work, aren't we missing a way of annotating a function so that it is clear from the outside that the derivatives will not depend onx
in a differentiable manner?
Currently I would use zeroDerivative for that. It does require that x has zero derivative in order to compute the derivative, which is necessary if we want to differentiate for index reduction since otherwise the dirac-pulses will be incorrectly handled.
Second, even for the current
noDerivative
semantics, wouldn't it make more sense to associate this with the function as a whole rather than with a particularderivative
annotation?
Possibly, but to me we would still be so far from the goal that it doesn't really make sense to prioritize such a change.
First, if
noDerivative = x
as used in the example shouldn't really work, aren't we missing a way of annotating a function so that it is clear from the outside that the derivatives will not depend onx
in a differentiable manner?Currently I would use zeroDerivative for that. It does require that x has zero derivative in order to compute the derivative, which is necessary if we want to differentiate for index reduction since otherwise the dirac-pulses will be incorrectly handled.
The problem with zeroDerivative
is that it is a constraint for the applicability of one of the derivatives; it says nothing about the function in general.
(On a side note, I encountered this when looking around for noDerivative
usage in the MSL: https://github.com/modelica/ModelicaStandardLibrary/issues/4023)
Please excuse a bit of scope creep instead of opening a new issue for getting my head around noDerivative
.
While the example in the specification makes sense to me (the example where the function input with noDerivative
is required to be a specific function of another input), I struggle with what seems to be a common usage pattern in the MSL. The simplest variant of this is probably Modelica.Blocks.Interfaces.Adaptors.Functions.state1
:
function state1 "Return state (with one derivative)"
extends Modelica.Icons.Function;
input Real u[2] "Required values for state and der(s)";
input Real dummy "Just to have one input signal that should be differentiated to avoid possible problems in the Modelica tool (is not used)";
output Real s;
algorithm
s := u[1];
annotation(derivative(noDerivative = u) = state1der1, InlineAfterIndexReduction = true);
end state1;
function state1der1 "Return 1st derivative (der of state1)"
extends Modelica.Icons.Function;
input Real u[2] "Required values for state and der(s)";
input Real dummy "Just to have one input signal that should be differentiated to avoid possible problems in the Modelica tool (is not used)";
input Real dummy_der;
output Real sder1;
algorithm
sder1 := u[2];
annotation(InlineAfterIndexReduction = true);
end state1der1;
I understand the description for x
("Required values for state and der(s)") by noting that s = x[1]
. That is, I read the description like this:
Vector of {s, der(s)} (where 's' is the "state")
What I can't understand is how to make sense of this when der(s)
is the time derivative of s
, differentiation in Modelica isn't only about time differentiation. What tells me that the provided state1der1
is going to produce wrong results when computing the partial derivative with respect to the "state"? Are we relying on x[1]
actually being a state determined by numeric integration as a guarantee for never differentiating the call partially with respect to this variable? Or is it the intricate interplay with InlineAfterIndexReduction = true
that is supposed to guarantee that the noDerivative
handling is gone by the time it comes to partial differentiation?
Are we relying on
x[1]
actually being a state determined by numeric integration as a guarantee for never differentiating the call partially with respect to this variable?
When using this function it is implicitly assumed that x[1]
is "known" so we don't have to solve for it.
For the h_dT
it is more complicated.
Or is it the intricate interplay with
InlineAfterIndexReduction = true
that is supposed to guarantee that thenoDerivative
handling is gone by the time it comes to partial differentiation?
And that as well.
We're now talking about two orthogonal matters in this conversation.
- The complicated nature of
noDerivative
. Interested readers are recommended to read this issue for additional background: https://github.com/modelica/ModelicaStandardLibrary/issues/1523 Here's a section I'd like to highlight, and that I don't see reflected by the current specification:
The Move block has the additional feature that the "noDerivative" annotation is used (because the "u" vector argument shall not be differentiated when function position(u) is differentiated). Sven Erik and Hans propose to utilize this feature: If annotation Inline=false is present, then the tool should avoid to inline according to the specification. However, if state selection fails and the function has the "noDerivative" annotation, then this is an indication that a situation as for the Move block is present. It is then proposed to make another try and inline the function before the state selection. However, this approach will not work for the current Move block, because the block has an additional, second dummy argument, "called dummy"). When the Move block is called, actually "time" is given for this dummy argument ("phi = position(u,time)"). I do no longer understand why this dummy argument is needed. Sven Erik and Hans propose to just remove it. If it is removed, then there remains just one argument with the "noDerivative" annotation and the sketched approach can be used.
- The observation that the
noDerivative
information – whatever it means – is a property of the function being differentiated, not just a property of one of the provided derivatives.
We're now talking about two orthogonal matters in this conversation. ... 2. The observation that the
noDerivative
information – whatever it means – is a property of the function being differentiated, not just a property of one of the provided derivatives.
That isn't entirely correct.
In one sense it is an assumption(or property) of the function being called, so e.g., h_props_dT
assumes that the arguments are d
, T
, and aux=Air_Utilities.airBaseProp_dT(d, T)
.
That is assumed for both the function itself and when attempting to differentiate it.
However, if the assumption isn't satisfied the function itself may still "work", but the derivative will not be valid as a derivative for the function call. In that sense it is more a property of the possible differentiation than of the function itself.
However, if the assumption isn't satisfied the function itself may still "work", but the derivative will not be valid as a derivative for the function call. In that sense it is more a property of the possible differentiation than of the function itself.
I suppose the ultimate question is whether it could ever make sense to provide two derivatives for the same function, but with different noDerivative
characteristics?
However, if the assumption isn't satisfied the function itself may still "work", but the derivative will not be valid as a derivative for the function call. In that sense it is more a property of the possible differentiation than of the function itself.
I suppose the ultimate question is whether it could ever make sense to provide two derivatives for the same function, but with different
noDerivative
characteristics?
Currently not. If noDerivative were able to specify the underlying assumption it would have worked (assuming that the assumptions could be verified), similarly as having a function with derivatives with different zeroDerivative.
If we were to associate noDerivative with the function being differentiated it would currently more be a case of stating that it is a non-independent input as in the examples above; but it seems like a quite complicated change for something that is already a hack.
I suppose the ultimate question is whether it could ever make sense to provide two derivatives for the same function, but with different
noDerivative
characteristics?Currently not. If noDerivative were able to specify the underlying assumption it would have worked (assuming that the assumptions could be verified), similarly as having a function with derivatives with different zeroDerivative.
Well, what a hack…