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

In this chapter, we will example different ways of implementing a simple one dimension interpolation scheme. We’ll start with an approach that is written completely in Modelica and then show an alternative implementation that combines Modelica with C. The advantages and disadvantages of each approach will then be discussed.

For this example, we assume that we interpolate data in the following form:

Independent Variable, | Dependent Variable, |

… | … |

where we assume that .

Given this data and the value for the independent variable that we are interested in, our function should return an interpolated value for . Such a function could be implemented in Modelica as follows:

```
function InterpolateVector "Interpolate a function defined by a vector"
input Real x "Independent variable";
input Real ybar[:,2] "Interpolation data";
output Real y "Dependent variable";
protected
Integer i;
Integer n = size(ybar,1) "Number of interpolation points";
Real p;
algorithm
assert(x>=ybar[1,1], "Independent variable must be greater than or equal to "+String(ybar[1,1]));
assert(x<=ybar[n,1], "Independent variable must be less than or equal to "+String(ybar[n,1]));
i := 1;
while x>=ybar[i+1,1] loop
i := i + 1;
end while;
p := (x-ybar[i,1])/(ybar[i+1,1]-ybar[i,1]);
y := p*ybar[i+1,2]+(1-p)*ybar[i,2];
end InterpolateVector;
```

Let’s go through this function piece by piece to understand what is going on. We’ll start with the argument declarations:

```
input Real x "Independent variable";
input Real ybar[:,2] "Interpolation data";
output Real y "Dependent variable";
```

The `input`

argument `x`

represents the value of the independent
variable we wish to use for interpolating our function, the `input`

argument `ybar`

represents the interpolation data and the `output`

argument `y`

represents the interpolated value. The next part of
the function contains:

```
protected
Integer i;
Integer n = size(ybar,1) "Number of interpolation points";
Real p;
```

The part of our function includes the declaration of various
`protected`

variables. As we saw in our
Polynomial Evaluation example, these are effectively
intermediate variables used internally by the function. In this case,
`i`

is going to be used as an index variable, `n`

is the number of
data points in our interpolation data and `p`

represents a weight
used in our interpolation scheme.

With all the variable declarations out of the way, we can now
implement the `algorithm`

section of our function:

```
algorithm
assert(x>=ybar[1,1], "Independent variable must be greater than or equal to "+String(ybar[1,1]));
assert(x<=ybar[n,1], "Independent variable must be less than or equal to "+String(ybar[n,1]));
i := 1;
while x>=ybar[i+1,1] loop
i := i + 1;
end while;
p := (x-ybar[i,1])/(ybar[i+1,1]-ybar[i,1]);
y := p*ybar[i+1,2]+(1-p)*ybar[i,2];
```

The first two statements are `assert`

statements that verify that
the value of `x`

is within the interval . If not,
an error message will be generated explaining why the interpolation
failed.

The rest of the function searches for the value of `i`

such that
. Once that value of `i`

has been identified,
the interpolated value is computed as simply:

where

Now, let’s test this function by using it from within a model. As a simple test case, let’s integrate the value returned by the interpolation function. We’ll use the following data as the basis for our function:

0 | 0 |

2 | 0 |

4 | 2 |

6 | 0 |

8 | 0 |

If we plot this data, we see that our interpolated function looks like this:

In the following model, the independent variable `x`

is set equal to
`time`

. The sample data is then used to interpolate a value for the
variable `y`

. The value of `y`

is then integrated to compute
`z`

.

```
model IntegrateInterpolatedVector "Exercises the InterpolateVector"
Real x;
Real y;
Real z;
equation
x = time;
y = InterpolateVector(x, [0.0, 0.0; 2.0, 0.0; 4.0, 2.0; 6.0, 0.0; 8.0, 0.0]);
der(z) = y;
annotation ...
end IntegrateInterpolatedVector;
```

We can see the simulated results from this model in the following plot:

There are a couple of drawbacks to this approach. The first is that
the data needs to be passed around anywhere the function is used.
Also, for higher dimensional interpolation schemes, the data required
can be both complex (for irregular grids) and large. So it is not
necessarily convenient to store the data in the Modelica source code.
For example, it might be preferable to store the data in an external
file. However, to populate the interpolation data from a source other
than the Modelica source code, we will need to use an
`ExternalObject`

.

`ExternalObject`

¶The `ExternalObject`

type is a special type used to refer to
information that is not (necessarily) represented in the Modelica
source code. The main use of the `ExternalObject`

type is to
represent data or state that is maintained outside the Modelica source
code. This might be interpolation data, as we will see in a moment,
or it might represent some other software system that maintains its
own state.

For this example, we will flip things around and start with the test
case. This will provide some useful context about how an
`ExternalObject`

is used. The Modelica source code for our test
case is:

```
model IntegrateInterpolatedExternalVector
"Exercises the InterpolateExternalVector"
parameter VectorTable vector = VectorTable(ybar=[0.0, 0.0;
2.0, 0.0;
4.0, 2.0;
6.0, 0.0;
8.0, 0.0]);
Real x;
Real y;
Real z;
equation
x = time;
y = InterpolateExternalVector(x, vector);
der(z) = y;
annotation ...
end IntegrateInterpolatedExternalVector;
```

Here the main difference between this and our previous test case is
the fact that we don’t pass our data directly into the interpolation
function. Instead, we create a special variable `vector`

whose type
is `VectorTable`

. We’ll discuss exactly what a `VectorTable`

is
in a moment. But for now think of it as something that represents
(only) our interpolation data. Other than the creation of the
`vector`

object, the rest of the model is virtually identical to the
previous case except that we use the `InterpolateExternalVector`

function to perform our interpolation and we pass the `vector`

variable into that function in place of our raw interpolation data.

Simulating this model, we see that the results are exactly what we would expect when compared to our previous test case:

`ExternalObject`

¶To see how this most recent test case is implemented, we’ll first look
at how the `VectorTable`

type is implemented. As mentioned
previously, the `VectorTable`

is an `ExternalObject`

type. This
is a special type in Modelica that is used to represent what is often
called an “opaque” pointer. This means that the `ExternalObject`

represents some data that is not directly accessible (from Modelica).

In our case, we implement our `VectorTable`

type as:

```
type VectorTable "A vector table implemented as an ExternalObject"
extends ExternalObject;
function constructor
input Real ybar[:,2];
output VectorTable table;
external "C" table=createVectorTable(ybar, size(ybar,1))
annotation ...
end constructor;
function destructor "Release storage"
input VectorTable table;
external "C" destroyVectorTable(table)
annotation ...
end destructor;
end VectorTable;
```

Note that the `VectorTable`

inherits from the `ExternalObject`

type. An `ExternalObject`

can have two special functions
implemented inside its definition, the `constructor`

function and
the `destructor`

function. Both of these functions are seen here.

The `constructor`

function is invoked when an instance of a
`VectorTable`

is created (*e.g.,* the declaration of the `vector`

variable in our test case). This `constructor`

function is used to
initialize our opaque pointer. Whatever data is required as part of
that initialization process should be passed as argument to the
`constructor`

function. That same data should be present during
instantiation (*.e.g,* the `data`

argument in our declaration of the
`vector`

variable).

The definition of the `constructor`

function is unusual because,
unlike our previous examples, it does not include an `algorithm`

section. The `algorithm`

section is normally used to compute the
return value of the function. Instead, the `constructor`

function
has an `external`

clause. This indicates that the function is
implemented in some other language besides Modelica. In this case,
that other language is C (as indicated by the `"C"`

following the
`external`

keyword). This tells use that the `table`

variable
(which is the `output`

of this function and represents the opaque
pointer) is returned by a C function named `createVectorTable`

which
is passed the contents and size of the `ybar`

variable.

Following the call to `createVectorTable`

is an `annotation`

.
This annotation tells the Modelica compiler where to find the source
code for this external C function.

The essential point here is that from the point of view of the
Modelica compiler, a `VectorTable`

is just an opaque pointer
returned by `createVectorTable`

. It is not possible to access the
data behind this pointer from Modelica. But this pointer can be
passed to other functions, as we shall see in a minute, that are also
implemented in C and which do know how to access the data represented
by the `VectorTable`

.

The `destructor`

function is invoked whenever the `ExternalObject`

is no longer needed. This allows the Modelica runtime to clean up any
memory consumed by the `ExternalObject`

. An `ExternalObject`

instantiated in a model will generally persist until the end of the
simulation. But an `ExternalObject`

declared as a `protected`

variable in a function, for example, may be created and destroyed in
the course of a single expression evaluation. For that reason, it is
important to make sure that any memory allocated by the
`ExternalObject`

is released.

In general, the `destructor`

function is also implemented as an
external function. In this case, calling the `destructor`

function
from Modelica invokes the C function `destroyVectorTable`

which is
passed a `VectorTable`

instance as an argument. Any memory
associated with that `VectorTable`

instance should be freed by the
call to `destructor`

. Again, we see the same types of annotations
used to inform the Modelica compiler where to find the source code for
the `destoryVectorTable`

function.

These external C functions are implemented as follows:

```
#ifndef _VECTOR_TABLE_C_
#define _VECTOR_TABLE_C_
#include <stdlib.h>
#include "ModelicaUtilities.h"
/*
Here we define the structure associated
with our ExternalObject type 'VectorTable'
*/
typedef struct {
double *x; /* Independent variable values */
double *y; /* Dependent variable values */
size_t npoints; /* Number of points in this data */
size_t lastIndex; /* Cached value of last index */
} VectorTable;
void *
createVectorTable(double *data, size_t np) {
VectorTable *table = (VectorTable*) malloc(sizeof(VectorTable));
if (table) {
/* Allocate memory for data */
table->x = (double*) malloc(sizeof(double)*np);
if (table->x) {
table->y = (double*) malloc(sizeof(double)*np);
if (table->y) {
/* Copy data into our local array */
size_t i;
for(i=0;i<np;i++) {
table->x[i] = data[2*i];
table->y[i] = data[2*i+1];
}
/* Initialize the rest of the table object */
table->npoints = np;
table->lastIndex = 0;
}
else {
free(table->x);
free(table);
table = NULL;
ModelicaError("Memory allocation error\n");
}
}
else {
free(table);
table = NULL;
ModelicaError("Memory allocation error\n");
}
}
else {
ModelicaError("Memory allocation error\n");
}
return table;
}
void
destroyVectorTable(void *object) {
VectorTable *table = (VectorTable *)object;
if (table==NULL) return;
free(table->x);
free(table->y);
free(table);
}
double
interpolateVectorTable(void *object, double x) {
VectorTable *table = (VectorTable *)object;
size_t i = table->lastIndex;
double p;
ModelicaFormatMessage("Request to compute value of y at %g\n", x);
if (x<table->x[0])
ModelicaFormatError("Requested value of x=%g is below the lower bound of %g\n",
x, table->x[0]);
if (x>table->x[table->npoints-1])
ModelicaFormatError("Requested value of x=%g is above the upper bound of %g\n",
x, table->x[table->npoints-1]);
while(i<table->npoints-1&&x>table->x[i+1]) i++;
while(i>0&&x<table->x[i]) i--;
p = (x-table->x[i])/(table->x[i+1]-table->x[i]);
table->lastIndex = i;
return p*table->y[i+1]+(1-p)*table->y[i];
}
#endif
```

This is not a book on the C programming language so an exhaustive review of this code and exactly how it functions is beyond the scope of the book. But we can summarize the contents of this file as follows.

First, the `struct`

called `VectorTable`

is the data associated
wit the `VectorTable`

type in Modelica. This includes not just the
interpolation data (in the form of the `x`

and `y`

members), but
also the number of data points, `npoints`

, and a cached value for
the last used index, `lastIndex`

.

Next, we see the `createVectorTable`

function which allocates an
instance of the `VectorTable`

structure and initializes all the data
inside it. That instance is then returned to the Modelica runtime.
Following the definition of `createVectorTable`

is the definition of
`destroyVectorTable`

which effectively undoes what was done by
`createVectorTable`

.

Finally, we see the function `interpolateVectorTable`

. This is a C
function that is passed an instance of the `VectorTable`

structure
and a value for the independent variable and returns the interpolated
value for the dependent variable. This function performs almost
exactly the same function as the `InterpolateVector`

function
presented earlier. The Modelica runtime provides functions like
`ModelicaFormatError`

so that external C code can report errors. In
the case of `interpolateVectorTable`

, these functions are used to
implement the assertions we saw previously in `InterpolateVector`

.
The lookup of `i`

is basically the same except that instead of
starting from 1 each time, it starts from the value of `i`

found in
the last call to `interpolateVectorTable`

.

We’ve seen how `interpolateVectorTable`

is defined, but so far we
haven’t seen where it is used. We mentioned that performs very much
the same role as `InterpolateVector`

, but using a `VectorTable`

object to represent the interpolation data. To invoke
`interpolateVectorTable`

from Modelica, we simple need to define a
Modelica function as follows:

```
function InterpolateExternalVector
"Interpolate a function defined by a vector using an ExternalObject"
input Real x;
input VectorTable table;
output Real y;
external "C" y = interpolateVectorTable(table, x)
annotation ...
end InterpolateExternalVector;
```

We mentioned previously that `VectorTable`

is opaque and that
Modelica code cannot access the data contained in the
`VectorTable`

. The Modelica function `InterpolateExternalVector`

invokes its C counterpart `interpolateVectorTable`

which **can**
access the interpolation data and, therefore, perform the interpolation.

As was discussed previously, the initial interpolation approach
required us to pass around large amounts of unwieldy data. By
implementing the `VectorTable`

, we were able to represent that data
by a single variable.

An important thing to note about the `ExternalObject`

approach,
which isn’t adequately explored in our example, is that the
initialization data can be completely external to the Modelica source
code. For simplicity, the example code shown in this section
initializes the `VectorTable`

using an array of data. **But it
could just as easily have passed a file name** to the initialization
code. That file could then have been read by the
`createVectorTable`

function and the contents of the `VectorTable`

structure could have been initialized using the data from that file.
In many cases, this approach not only makes managing the data easier,
but leveraging C allows more complex (new or existing) algorithms to
be used.

The next section includes another example of how external C code can be called from Modelica.