There are a great many functions in Modelica that are related to arrays. In this section, we’ll go through different categories of functions and describe how they are used.
We already talked about Array Construction. We saw the different syntactic constructs that can be used to build vectors and matrices. Furthermore, we saw how matrices can be built from other matrices. There are several functions in Modelica that can be used for constructing vectors, matrices and higher-dimension arrays as both an alternative or complement to those previous presented.
fill
¶The fill
function is used to create an array where each element in
the array has the same value. The arguments for fill
are:
fill(v, d1, d2, ..., dN)
where v
is the value to be given to each element in the array and
the remaining arguments are the sizes of each dimension. The elements
in the resulting array will have the same type as v
. So, to fill
a 5x7 array of reals with the value 1.7
, we could use the
following:
parameter Real x[5,7] = fill(1.7, 5, 7);
This would result in a matrix filled as follows:
zeros
¶When working with arrays, a common use case is to create an array that
contains only zero elements. This is essentially the same
functionality as the fill
function, but since the value is known
it is only necessary to specify the sizes. Using zeros
we can
initialize an array as follows:
parameter Real y[2,3,5] = zeros(2, 3, 5);
ones
¶The ones
function is identical to the zeros
function except
that every element in the resulting array has the value .
So, for example:
parameter Real z[3,5] = ones(3, 5);
This would result in a matrix filled as follows:
identity
¶Another common need is to easily build an identity matrix, one whose
diagonal elements are all while all other elements are
. This can be done very easily with the identity
. The
identity function takes a single integer argument. This argument
determines the number of rows and columns in the resulting matrix.
So, invoking identity
as:
identity(5);
would produce the following matrix:
diagonal
¶The diagonal
function is used to create a matrix where all
non-diagonal elements are . The only argument to diagonal is
an array containing the values of the diagonal elements. So, to
create the following diagonal matrix:
one could use the following Modelica code:
diagonal({2.0, 3.0, 4.0, 5.0});
linspace
¶The linspace
function builds a vector where the values of the
elements are all linearly distributed over an interval. The
linspace
function is invoked as follows:
linspace(v0, v1, n);
where v0
is the value of the first elements in the vector, v1
is the last element in the vector and n
is the total number of
values in the vector. So, for example, invoking linspace
as:
linspace(1.0, 5.0, 9);
would yield the vector:
{1.0, 1.5, 2.0, 3.5, 3.0, 3.5, 4.0, 4.5, 5.0}
The following functions provide a means to transform arrays into other arrays.
scalar
¶The scalar
function is invoked as follows:
scalar(A)
where A
is an array with an arbitrary number of dimensions as long
as each dimension is of size . The scalar
function
returns the (only) scalar value contained in the array. For example,
scalar([5]) // Argument is a two-dimensional array (matrix)
and
scalar({5}) // Argument is a one-dimensional array (vector)
would both give the scalar value 5
.
vector
¶The vector
function is invoked as follows:
vector(A)
where A
is an array with an arbitrary number of dimensions as long
as only one dimension has a size greater than . The
vector
function returns the contents of the array as a vector
(i.e., an array with only a single dimension). So, for example, if
we passed a column or row matrix, e.g.,
vector([1;2;3;4]) // Argument is a column matrix
or
vector([1,2,3,4]) // Argument is a row matrix
we would get back:
{1,2,3,4}
matrix
¶The matrix
function is invoked as follows:
matrix(A)
where A
is an array with an arbitrary number of dimensions as long
as only two dimension have a size greater than . The
matrix
function returns the contents of the array as a matrix
(i.e., an array with only two dimensions).
In linear algebra, there are many different types of mathematical operations that are commonly performed on vectors and matrices. Modelica provides functions to perform most of these operations. In this way, Modelica equations can be made to look very much like their mathematical counterparts in linear algebra.
Let’s start with operations like addition, subtraction, multiplication, division and exponentiation. For the most part, these operations work just as they do in mathematics when applied to the various combinations of scalars, vectors and matrices. However, for completeness and reference, the following tables summarize how these operations are defined.
Explanation of Notation
Each of the operations described below involves two arguments, and , and a result, . If an argument represents a scalar, it will have no subscripts. If it is a vector, it will have one subscript. If it is a matrix, it will have two subscripts. If the operation is defined for arbitrary arrays, a case will be included with three subscripts. If a given combination is not shown, then it is not allowed.
+
)¶Expression | Result |
-
)¶Expression | Result |
*
and .*
)¶There are two types of multiplication operators. The first is the
normal multiplication operator, *
, that follows the usual
mathematical conventions of linear algebra that matrix-vector
products, etc.. The behavior of the *
operator is summarized in
the following table:
Expression | Result |
The second type of multiplication operator is a special element-wise
version, .*
, which doesn’t perform any summations and simply
applies the operator element-wise to all array elements.
Expression | Result |
.* |
|
.* |
|
.* |
|
.* |
/
and ./
)¶As with Multiplication (* and .*), there are two division
operators. The first is the normal division operator, /
, which
can be used to divide arrays by a scalar value. The following table
summarizes its behavior:
Expression | Result |
In addition, there is also an element-wise version of the division
operator, ./
, whose behavior is summarized in the following table:
Expression | Result |
./ |
|
./ |
|
./ |
|
./ |
^
and .^
)¶As with Multiplication (* and .*) and Division (/ and ./), the
exponentiation operator comes in two forms. The first is the standard
exponentiation operator, ^
. The standard version can be used in
two different ways. The first is to raise one scalar to the power of
another (i.e., ^
). The other is to raise a
square matrix to a scalar power (i.e., ^
).
The other form of exponentiation is the element-wise form indicated
with the .^
operator. Its behavior is summarized in the following
table:
Expression | Result |
.^ |
|
.^ |
|
.^ |
|
.^ |
=
)¶The equality operator, =
used to construct equations can be used
with scalars as well as arrays as long as the left hand side and
right hand side have the same number of dimensions and the sizes of
each dimension are the same. Assuming this requirement is met, then
the operator is simply applied element-wise. This means that the
operator is applied between each element on the left hand side and its
counterpart on the right hand side.
:=
)¶The :=
(assignment) operator is applied in the same element-wise
way as the Equality (=) operator.
All relational operators (and
, or
, not
, >
, >=
,
<=
, <
) are applied in the same element-wise way as the
Equality (=) operator.
transpose
¶The transpose
function takes a matrix as an argument and returns a
transposed version of that matrix.
outerProduct
¶The outerProduct
function takes two arguments. Each argument must
be a vector and they must have the same size. The function returns a
matrix which represents the outer product of the two vectors.
Mathematically speaking, assume and are vectors of the
same size. Invoking outerProduct(a,b)
will return a matrix,
, whose elements are defined as:
symmetric
¶The symmetric
function takes a square matrix as an argument. It
returns a matrix of the same size where all the elements below the
diagonal of the original matrix have been replaced by elements
transposed from above the diagonal. In other words,
skew
¶The skew
function takes a vector with three components and returns
the following skew-symmetric matrix:
cross
¶The cross
function takes two vectors (each with 3 components) and
returns the following vector (with three components):
Reduction operators are ones that reduce arrays down to scalar values.
min
¶The min
function takes an array and returns the smallest value in
the array. For example:
min({10, 7, 2, 11}) // 2
min([1, 2; 3, -4]) // -4
max
¶The max
function takes an array and returns the largest value in
the array. For example:
max({10, 7, 2, 11}) // 11
max([1, 2; 3, -4]) // 3
sum
¶The sum
function takes an array and returns the sum of all
elements in the array. For example:
sum({10, 7, 2, 11}) // 30
sum([1, 2; 3, -4]) // 2
product
¶The product
function takes an array and returns the product of all
elements in the array. For example:
product({10, 7, 2, 11}) // 1540
product([1, 2; 3, -4]) // -24
ndims
¶The ndims
function takes an array as its argument and returns the
number of dimensions in that array. For example:
ndims({10, 7, 2, 11}) // 1
ndims([1, 2; 3, -4]) // 2
size
¶The size
function can be invoked two different ways. The first
way is with a single argument that is an array. In this case,
size
returns a vector where each component in the vector
corresponds to the size of the corresponding dimension in the array. For example:
size({10, 7, 2, 11}) // {4}
size([1, 2, 3; 3, -4, 5]) // {2, 3}
It is also possible to call size
with an optional additional
argument indicating a specific dimension number. In that case, it
will return the size of that specific dimension as a scalar integer.
For example,
size({10, 7, 2, 11}, 1) // 4
size([1, 2, 3; 3, -4, 5], 1) // 2
size([1, 2, 3; 3, -4, 5], 2) // 3
In this section, we’ve discussed the numerous functions in Modelica that are designed to work with arguments that are arrays. But a very common use case is to apply a function element-wise to every element in a vector. Modelica supports this use case through a feature called “vectorization”. If a function is designed to take a scalar, but is passed an array instead, the Modelica compiler will automatically apply that function to each element in the vector.
To understand how this works, first consider a normal evaluation using
the abs
function:
abs(-1.5) // 1.5
Obviously, abs
is normally meant to accept a scalar argument and
return a scalar. But in Modelica, we can also do this:
abs({0, -1, 1, -2, 2}) // {0, 1, 1, 2, 2}
Since this function is designed for scalar, the Modelica compiler will transform:
abs({0, -1, 1, -2, 2})
into
{abs(0), abs(-1), abs(1), abs(-2), abs(2)}
In other words, it transforms the function applies to a vector of scalars into a vector a functions applied to scalar.
This feature also works functions that take multiple arguments as
long as only one of the expected scalar arguments is a vector. To
understand this slightly more complex functionality, consider the
modulo function, mod
. If applied to scalar arguments we get the
following behavior:
mod(5, 2) // 1
If we turn the first argument into a vector, we get:
mod({5, 6, 7, 8}, 2) // {1, 0, 1, 0}
In other words, it transforms:
mod({5, 6, 7, 8}, 2)
into
{mod(5,2), mod(6,2), mod(7,2), mod(8,2)}
However, this vectorization does not apply if more than one scalar arguments is presented as a vector. For example, the following expression will be an error:
mod({5, 6, 7, 8}, {2, 3}) // Illegal
because mod
expects two scalar arguments, but it was passed two
vector arguments.