Found an issue with the book? Report it on Github.

Our first example will center around using functions to evaluate polynomials. This will help use understand the basics of defining and using functions.

Before diving until polynomials of arbitrary order, let’s first consider how we could use a function to evaluate points on a line. Mathematically, what we’d like to define is a function that is applied as follows:

where is the independent variable, is one point that defines the line and is the other point that defines the line. Mathematically, such a function could be defined as follows:

To reduce the number of arguments, let’s assume that combine and into a single point represented by the vector and we combine and into a single point represented by the vector so that the function is now invoked as:

The question now is how can we transform this mathematical relationship into a function that we can invoke from within a Modelica model. To do this, we must define a new Modelica function.

It turns out that a function definition is very similar
(syntactically, at least) to a Model Definition. Here is the
definition of our `Line`

function in Modelica:

```
function Line "Compute coordinates along a line"
input Real x "Independent variable";
input Real p0[2] "Coordinates for one point on the line";
input Real p1[2] "Coordinates for another point on the line";
output Real y "Value of y at the specified x";
algorithm
y := (p1[2]-p0[2])/(p1[1]-p0[1])*(x-p0[1])+p0[2];
end Line;
```

All the arguments to the function are prefixed with the `input`

qualifier. The result of the function has the `output`

qualifier.
The body of the function is an `algorithm`

section. The value for
the return value (`y`

in this case) is computed by the `algorithm`

section.

So in this case, the `output`

value, `y`

, is computed in terms of
the `input`

values `x`

, `p0`

and `p1`

. Note that there is no
`return`

statement in this function. Whatever the value of the
`output`

variable is at the conclusion of the `algorithm`

section
is automatically the value returned.

A couple of things to note that were discussed in previous chapters. First, note the descriptive strings on both the function itself and the arguments. These are very useful in documenting the purpose of the function and its arguments. Also note how the points use arrays to represent a two-dimensional vector and how those arrays are indexed in this example.

One troubling aspect of the `Line`

model is the length and
complexity of the expression used to compute `y`

. It would be nice
if we could break that expression down.

In order to simplify the expression for `y`

, we need to introduce
some intermediate variables. We can already see that `x`

, `p0`

and `p1`

are variables that we can use from within the function.
We’d like to introduce additional variables, but they shouldn’t be
arguments. Instead, their values should be computed “internally” to
the function. To achieve this, we create a collection of variables
that are `protected`

. Such variables are assumed to be computed
internally by the function. Here is an example that uses
`protected`

to declare and compute two internal variables:

```
function LineWithProtected "The Line function with protected variables"
input Real x "Independent variable";
input Real p0[2] "Coordinates for one point on the line";
input Real p1[2] "Coordinates for another point on the line";
output Real y "Value of y at the specified x";
protected
Real x0 = p0[1], x1 = p1[1];
Real y0 = p0[2], y1 = p1[2];
Real m = (y1-y0)/(x1-x0) "Slope";
Real b = (y0-m*x0) "Offset";
algorithm
y := m*x+b;
end LineWithProtected;
```

This model introduces two new variables. One variable, `m`

,
represents the slope of the line and the other, `b`

, represents the
return value for the condition when `x=0`

. Having computed these
two intermediate variables, the expression to evaluate `y`

becomes
the more easily recognized form `y := m*x+b`

.

Of course, our goal for this section is to create a function that can compute arbitrary polynomials. So now that we’ve seen a few basic functions, let us proceed with our ultimate goal. We will formulate a function that is invoked as follows:

where is again the independent variable and is a vector of coefficients such that our polynomial is evaluated as:

where `N`

is the number of coefficients passed to the function.
There are two important things to note at this point. First, **the
first element in** **corresponds to the highest order term
in the polynomial**. Second, we are using a notation that assumes
that the elements in are numbered **starting from 1**
to make the transition to Modelica code (where arrays are indexed
starting from 1) easier.

Note that the definition for above is easy to read and understand. But when working with floating point numbers with finite precision, it is more efficient and more accurate to use a recursive approach for evaluating the polynomial. For a order polynomial, the evaluation would be:

This is more efficient because it relies on simple multiplication and addition operations and avoids performing exponentiation operations, which are more expensive, It is more accurate because exponentiation can easily trigger round-off or truncation errors in finite precision floating point representations.

Now that we’ve defined precisely what computations we want the function to perform, we are just left with the task of defining the function in Modelica. In this case, our polynomial evaluation function can be represented in Modelica as:

```
function Polynomial "Create a generic polynomial from coefficients"
input Real x "Independent variable";
input Real c[:] "Polynomial coefficients";
output Real y "Computed polynomial value";
protected
Integer n = size(c,1);
algorithm
y := c[1];
for i in 2:n loop
y := y*x + c[i];
end for;
end Polynomial;
```

Again, all the arguments to the function have the `input`

qualifier
and the return value has the `output`

qualifier. As with the
previous example, we’ve defined an intermediate variable, `n`

, as a
convenient way to refer to the length of the coefficient vector. We
also see how a `for`

loop can be used to represent the recursive
evaluation of our polynomial for any arbitrary order.

To verify that this function is working properly, let’s use it in a model. Consider the following Modelica model:

```
model EvaluationTest1 "Model that evaluates a polynomial"
Real yf;
Real yp;
equation
yf = Polynomial(time, {1, -2, 2});
yp = time^2-2*time+2;
end EvaluationTest1;
```

Remember that the first element in `c`

corresponds to the highest
order term. If we compare a direct evaluation of the polynomial,
`yp`

, with one computed by our function, `yf`

, we see they are identical:

It is completely plausible that this polynomial evaluation might be used to represent a quantity that was ultimately differentiated by the Modelica compiler. The following examples is admittedly contrived, but it demonstrates how such a polynomial might come to be differentiated in a model:

```
model Differentiation1 "Model that differentiates a function"
Real yf;
Real yp;
Real d_yf;
Real d_yp;
equation
yf = Polynomial(time, {1, -2, 2});
yp = time^2-2*time+2;
d_yf = der(yf); // How to compute?
d_yp = der(yp);
end Differentiation1;
```

Here we have the same equations for `yf`

, evaluated using
`Polynomial`

, and `yp`

, evaluated directly as a polynomial. But
we’ve added two additional variables, `d_yf`

and `d_yp`

representing the derivative of `yf`

and `yp`

, respectively. If we
attempt to compile this model the compiler is very likely to throw an
error related to the equation for `d_yf`

. The reason is that it has
no way to compute the derivative of `yf`

. This is because, unlike
`yp`

which is computed with a simple expression, we’ve hidden the
details of how `yf`

is computed behind the function `Polynomial`

.
In general, Modelica tools do not look at the implementations of
functions to compute derivatives and, even if they did, determining
the derivative of an arbitrary algorithm is not an easy thing to do.

So the next question is how can we deal with this situation? Won’t
this make it difficult to use our functions within models?
Fortunately, Modelica gives us a way to specify how to evaluate the
derivative of a function. This is done by adding something called an
`annotation`

to the function definition.

Annotations

An annotation is a piece of metadata that doesn’t describe the
behavior of the function directly (*i.e.,* it doesn’t affect the
value the function returns). Instead, annotations are used by
Modelica compilers to give them “hints” about how to deal with
certain situations. Annotations are always “optional” information
which means tools are not required to use the information when
provided. The Modelica specification defines a number of standard
annotations so that they are interpreted consistently across
Modelica tools.

In this case, what we need is the `derivative`

annotation because it
will allow us to communicate information to the Modelica compiler on
how to evaluate the derivative of our function. To do this, we define
a new evaluation function, `PolynomialWithDerivative`

, as follows:

```
function PolynomialWithDerivative
"Create a generic polynomial from coefficients (with derivative information)"
input Real x "Independent variable";
input Real c[:] "Polynomial coefficients";
output Real y "Computed polynomial value";
protected
Integer n = size(c,1);
algorithm
y := c[1];
for i in 2:n loop
y := y*x + c[i];
end for;
annotation(derivative=PolynomialFirstDerivative);
end PolynomialWithDerivative;
```

Note that this function is identical except for the highlighted line. In other words, all we needed to do was add the line:

```
annotation ...
```

to our function in order to explain to the Modelica compiler how to
evaluate the derivative of this function. What it indicates is that
the function `PolynomialFirstDerivative`

should be used to evaluate
the derivative of `PolynomialWithDerivative`

.

Before discussing the implementation of the
`PolynomialFirstDerivative`

function, let’s first understand,
mathematically, what is required. Recall our original definition of
our polynomial interpolation function:

Note that takes two arguments. If we wish to differentiate by some arbitrary variable , we can use the chain rule to express the total derivative of with respect to as:

We can derive the following relations from our original definition of . First, for the partial derivative of with respect to we get:

where is defined as:

Second, for the partial derivative of with respect to we get:

where the **vector** is the column of
an identity matrix.

It turns out that for efficiency reasons, it is better for the Modelica compiler to give us and than to provide functions to evaluate and . So, mathematically speaking, what the Modelica compiler needs is a new function that is invoked with the following arguments:

such that:

For this reason, the `derivative`

annotation should point to a
function that takes the same arguments as . In our case,
that function, `PolynomialFirstDerivative`

would be defined as
follows:

```
function PolynomialFirstDerivative
"First derivative of the function Polynomial"
input Real x;
input Real c[:];
input Real x_der;
input Real c_der[size(c,1)];
output Real y_der;
protected
Integer n = size(c,1);
Real c_diff[n-1] = {(n-i)*c[i] for i in 1:n-1};
algorithm
y_der :=PolynomialWithDerivative(x, c_diff)*x_der +
PolynomialWithDerivative(x, c_der);
end PolynomialFirstDerivative;
```

Note how the arguments of our original function are repeated to create twice as many arguments (as we would expect). The second set of arguments represent the and quantities, respectively. Note that the assumption is that is a scalar so the types of the input arguments are the same. Exploiting our knowledge about the partial derivatives of a polynomial, the calculation of the derivatives is done by leveraging our polynomial evaluation function.

We can exercise all of these functions using the following model:

```
model Differentiation2 "Model that differentiates a function using derivative annotation"
Real yf;
Real yp;
Real d_yf;
Real d_yp;
equation
yf = PolynomialWithDerivative(time, {1, -2, 2});
yp = time^2-2*time+2;
d_yf = der(yf);
d_yp = der(yp);
end Differentiation2;
```

Simulating this model and comparing results, we see agreement between
`yf`

and `yp`

as well as `d_yf`

and `d_yp`

: