This chapter describes the Modelica function construct.
A Modelica function is a specialized class (section 12.2) using the keyword function. The body of a Modelica function is an algorithm section that contains procedural algorithmic code to be executed when the function is called, or alternatively an external function specifier (section 12.9). Formal parameters are specified using the input keyword, whereas results are denoted using the output keyword. This makes the syntax of function definitions quite close to Modelica class definitions, but using the keyword function instead of class.
[The structure of a typical function declaration is sketched by the following schematic function example:
]
Optional explicit default values can be associated with any input or output formal parameter through declaration assignments. [Such defaults are shown for the third input parameter and the second output parameter in our example.] Comment strings and annotations can be given for any formal parameter declaration, as usual in Modelica declarations.
[All internal parts of a function are optional; i.e., the following is also a legal function:
]
The relative ordering between input formal parameter declarations is significant since that determines the matching between actual arguments and formal parameters at function calls with positional parameter passing. Likewise, the relative ordering between the declarations of the outputs is significant since that determines the matching with receiving variables at function calls of functions with multiple results. However, the declarations of the inputs and outputs can be intermixed as long as these internal orderings are preserved. [Mixing declarations in this way is not recommended, however, since it makes the code hard to read.]
[Example:
]
The return-statement terminates the current function call, see section 12.4. It can only be used in an algorithm section of a function. It has the following form:
[Example (note this could alternatively use break):
]
It is allowed for a function to inherit and/or modify another function following the usual rules for inheritance of classes (chapter 7). [For example, it is possible to modify and extend a function class to add default values for input variables.]
The function concept in Modelica is a specialized class (section 4.6). [The syntax and semantics of a function have many similarities to those of the block specialized class. A function has many of the properties of a general class, e.g. being able to inherit other functions, or to redeclare or modify elements of a function declaration.]
Modelica functions have the following restrictions compared to a general Modelica class:
Each input formal parameter of the function must be prefixed by the keyword input, and each result formal parameter by the keyword output. All public variables are formal parameters.
Input formal parameters are read-only after being bound to the actual arguments or default values, i.e., they may not be assigned values in the body of the function.
A function may not be used in connections, may not have equations, may not have initial algorithms.
A function can have at most one algorithm section or one external function interface (not both), which, if present, is the body of the function.
A function may only contain components of the restricted classes type, record, operator record, and function; i.e. no model or block components.
The elements of a function may not have prefixes inner, or outer.
A function may have zero or one external function interface, which, if present, is the external definition of the function.
For a function to be called in a simulation model, the function may not be partial, and the output variables must be assigned inside the function either in declaration assignments or in an algorithm section, or have an external function interface as its body, or be defined as a function partial derivative. The output variables of a function should be computed. [It is a quality of implementation how much analysis a tool performs in order to determine if the output variables are computed]. A function cannot contain calls to the Modelica built-in operators der, initial, terminal, sample, pre, edge, change, reinit, delay, cardinality, inStream, actualStream, to the operators of the built-in package Connections, to the operators defined in chapter 16 and chapter 17, and is not allowed to contain when-statements.
The dimension sizes not declared with (:) of each array result or array local variable [i.e., a non-input components] of a function must be either given by the input formal parameters, or given by constant or parameter expressions, or by expressions containing combinations of those (section 12.4.4).
For initialization of local variables of a function see section 12.4.4).
Components of a function will inside the function behave as though they had discrete-time variability.
Modelica functions have the following enhancements compared to a general Modelica class:
Functions can be called, section 12.4.
The calls can use a mix of positional and named arguments, see section 12.4.1.
Instances of functions have a special meaning, see section 12.4.2.
The lookup of the function class to be called is extended, see section 5.3.2.
A function can be recursive.
A formal parameter or local variable may be initialized through a binding (=) of a default value in its declaration, see section 12.4.4. Using assignment (:=) is deprecated. If a non-input component in the function uses a record class that contain one or more binding equations they are viewed as initialization of those component of the record component.
A function is dynamically instantiated when it is called rather than being statically instantiated by an instance declaration, which is the case for other kinds of classes.
A function may have an external function interface specifier as its body.
A function may have a return statement in its algorithm section body.
A function allows dimension sizes declared with (:) to be resized for non-input array variables, see section 12.4.5.
A function may be defined in a short function definition to be a function partial derivative.
Modelica functions are normally pure which makes it easy for humans to reason about the code since they behave as mathematical functions, and possible for compilers to optimize.
Pure Modelica functions always give the same output values or errors for the same input values and only the output values influence the simulation result, i.e. is seen as equivalent to a mathematical map from input values to output values. Some input values may map to errors. Pure functions are thus allowed to fail by calling assert, or ModelicaError in C-code, or dividing by zero. Such errors will only be reported when and if the function is called.Pure Modelica functions are not assumed to be thread-safe.
A Modelica function which does not have the pure function properties is impure.
The declaration of functions follow these rules:
Functions defined in Modelica (non-external) are normally assumed to be pure (the exception is the deprecated case below), if they are impure they shall be marked with the impure keyword. They can be explicitly marked as pure. [However, since functions as default are pure it is not recommended to explicitly declare them as pure.]
External functions must be explicitly declared with pure or impure.
A deprecated semantics is that external functions (and functions defined in Modelica directly or indirectly calling them) without pure or impure keyword are assumed to be impure – but without any restriction on calling them. Except for the function Modelica.Utilities.Streams.print diagnostics must be given if called in a simulation model.
Calls of pure functions used inside expression may be skipped if the resulting expression will not depend on the possible returned value; ignoring the possibility of the function generating an error.
A call to a function with no declared outputs is assumed to have desired side-effects or assertion checks. [A tool shall thus not remove such function calls, with exception of non-triggered assert calls. A pure function, used in an expression or used with a non-empty left hand side, need not be called if the output from the function call do not mathematically influence the simulation result, even if errors would be generated if it were called.]
[Comment 1: This property enables writing declarative specifications using Modelica. It also makes it possible for Modelica compilers to freely perform algebraic manipulation of expressions containing function calls while still preserving their semantics. For example, a tool may use common subexpression elimination to call a pure function just once, if it is called several times with identical input arguments. However, since functions may fail we can e.g. only move a common function call from inside a loop to outside the loop if the loop is run at least once.]
[Comment 2: The Modelica translator is responsible for maintaining this property for pure non-external functions. Regarding external functions, the external function implementor is responsible. Note that external functions can have side-effects as long as they do not influence the internal Modelica simulation state, e.g. caching variables for performance or printing trace output to a log file.]
With the prefix keyword impure it is stated that a Modelica function is impure and it is only allowed to call such a function from within:
another function marked with the prefix impure
a when-equation,
a when-statement,
pure(impureFunctionCall(...)) - which allows calling impure functions in any pure context,
in initial equations and initial algorithms,
in bindings for variables declared as parameter – which is seen as syntactic sugar for having a parameter with fixed=false and the binding as an initial equation [thus there is no guarantee that parameter is equal to the impure function call after initialization] – and in constructing external objects.
For initial equations, initial algorithms, and bindings it is an error if the function calls are part of systems of equations and thus have to be called multiple times.
[A tool is not allowed to perform any optimizations on function calls to an impure function, e.g., reordering calls from different statements in an algorithm or common subexpression elimination is not allowed.]
It is possible to mark a function formal parameter as impure. Only if the function formal parameter is marked impure, it is allowed to pass an impure function to it. A function having a formal function parameter marked impure must be marked pure or impure.
[Comment: The semantics are undefined if the function call of an impure function is part of an algebraic loop.]
[Examples:
Examples of allowed optimizations of pure functions:
A tool only needs to generate one call of the pure function cos(w) in the model M – to handle the two elements of the matrix above, and for the derivative of that matrix. A tool may also skip the possible error for asin(w) and assume that a is zero.
Examples of restrictions on optimizing pure functions:
]
Function classes and record constructors (section 12.6) and enumeration type conversions (section 4.8.5.3) can be called as described in this section.
A function call has optional positional arguments followed by zero, one or more named arguments, such as
The formal syntax of a function call (simplified by removing reduction expression, section 10.3.4.1):
The interpretation of a function call is as follows: First, a list of unfilled slots is created for all formal input parameters. If there are N positional arguments, they are placed in the first N slots, where the order of the parameters is given by the order of the component declarations in the function definition. Next, for each named argument identifier = expression, the identifier is used to determine the corresponding slot. This slot shall be not filled [otherwise an error occurs] and the value of the argument is placed in the slot, filling it. When all arguments have been processed, the slots that are still unfilled are filled with the corresponding default value of the function definition. The default values may depend on other inputs (these dependencies must be acyclical in the function) – the values for those other inputs will then be substituted into the default values (this process may be repeated if the default value for that input depend on another input). The default values for inputs may not depend on non-input variables in the function. There shall be no remaining unfilled slots [otherwise an error occurs] and the list of filled slots is used as the argument list for the call.
Special purpose operators with function syntax defined in the specification may not be called with named arguments, unless otherwise noted.
The type of each argument must agree with the type of the corresponding parameter, except where the standard type coercions can be used to make the types agree. (See also section 12.4.6 on applying scalar functions to arrays.)
[Example.
Assume a function RealToString is defined as follows to convert a Real number to a String:
Then the following applications are equivalent:
]
A functional input argument to a function is an argument of function type. The declared type of such an input formal parameter in a function can be the class-name of a partial function that has no replaceable elements. It cannot be the class-name of a record or enumeration [i.e., record constructor functions and enumeration type conversions are not allowed in this context.] Such an input formal parameter of function type can also have an optional functional default value.
[Example:
]
A functional argument can be provided in one of the following forms to be passed to a scalar formal parameter of function type in a function call:
as a function type-specifier [Parabola example below],
as a function partial application (section 12.4.2.1 below),
as a function that is a component,
as a function partial application of a function that is a component (example in section 12.4.2.1 below).
In all cases the provided function must be “function type compatible” (section 6.5) to the corresponding formal parameter of function type.
[Example:
A function as a positional input argument according to case (a)
The quadrature2 example below uses a function integrand that is a component as input argument according to case (c):
A function partial application is similar to a function call with certain formal parameters bound to expressions, the specific rules are specified in this section and are not identical to the ones for function call in section 12.4.1. A function partial application returns a partially evaluated function that is also a function, with the remaining not bound formal parameters still present in the same order as in the original function declaration. A function partial application is specified by the function keyword followed by a function call to func_name giving named formal parameter associations for the formal parameters to be bound, e.g.:
[Note that the keyword function in a function partial application differentiates the syntax from a normal function call where some parameters have been left out, and instead supplied via default values.]
The function created by the function partial application acts as the original function but with the bound formal input parameters(s) removed, i.e., they cannot be supplied arguments at function call. The binding occurs when the partially evaluated function is created. A partially evaluated function is “function compatible” (see section 6.5) to the same function where all bound arguments are removed [thus, for checking function type compatibility, bound formal parameters are ignored].
[Example of function partial application as argument, positional argument passing, according to case (b) above:
Call with function partial application as named input argument:
]
[Example showing that function types are matching after removing the bound arguments A and w in a function partial application:
The partially evaluated Sine2 has only one argument: x – and is thus type compatible with Integrand.
]
[Example of a function partial application of a function that is a component, according to case (d) above:
]
A function may have more than one output component, corresponding to multiple return values. The only way to use more than the first return value of such a function is to make the function call the right hand side of an equation or assignment. In this case, the left hand side of the equation or assignment shall contain a list of component references within parentheses:
(out1, out2, out3) = f(...);
The component references are associated with the output components according to their position in the list. Thus output component i is set equal to, or assigned to, component reference i in the list, where the order of the output components is given by the order of the component declarations in the function definition. The type of each component reference in the list must agree with the type of the corresponding output component.
A function application may be used as expression whose value and type is given by the value and type of the first output component, if at least one return result is provided.
It is possible to omit left hand side component references and/or truncate the left hand side list in order to discard outputs from a function call.
[Optimizations to avoid computation of unused output results can be automatically deduced by an optimizing compiler].
[Example:
Function ”eigen” to compute eigenvalues and optionally eigenvectors may be called in the following ways:
The function may be defined as:
]
The only permissible use of an expression in the form of a list of expressions in parentheses, is when it is used as the left hand side of an equation or assignment where the right hand side is an application of a function.
[Example. The following are illegal:
]
Components in a function can be divided into three groups:
Public components which are input formal parameters.
Public components which are output formal parameters.
Protected components which are local variables, parameters, or constants.
When a function is called components of a function do not have start-attributes. However, a declaration assignment (:= expression) with an expression may be present for a component.
A declaration assignment for a non-input component initializes the component to this expression at the start of every function invocation (before executing the algorithm section or calling the external function). These bindings must be executed in an order where a variable is not used before its declaration assignment has been executed; it is an error if no such order exists (i.e. the binding must be acyclic).
Declaration assignments can only be used for components of a function. If no declaration assignment is given for a non-input component the variable is uninitialized (except for record components where modifiers may also initialize that component). It is an error to use (or return) an uninitialized variable in a function. [It is recommended to check this statically - if this is not possible a warning is recommended combined with a run-time check.] Declaration assignments for input formal parameters are interpreted as default arguments, as described in section 12.4.1.
[The properties of components in functions described in this section are also briefly described in section 12.2.]
[Flexible setting of array dimension sizes of arrays in functions is also briefly described in section 12.2.]
A dimension size not specified with colon(:) for a non-input array component of a function must be given by the inputs or be constant.
[Example:
]
A non-input array component declared in a function with a dimension size specified by colon(:) and no declaration assignment, can change size according to these special rules:Prior to execution of the function algorithm the dimension size is zero.
The entire array (without any subscripts) may be assigned with a corresponding array with arbitrary dimension size (the array variable is re-sized).
These rules also apply if the array component is an element of a record component in a function.
[Example: A function to collect the positive elements in a vector:
]
Functions with one scalar return value can be applied to arrays element-wise, e.g. if A is a vector of reals, then sin(A) is a vector where each element is the result of applying the function sin to the corresponding element in A. Only function classes that are transitively non-replaceable (section 6.2.1 and section 7.1.4) may be called vectorized.
Consider the expression f(arg1,...,argn), an application of the function f to the arguments arg1,..., argn is defined.
For each passed argument, the type of the argument is checked against the type of the corresponding formal parameter of the function.
If the types match, nothing is done.
If the types do not match, and a type conversion can be applied, it is applied. Continue with step 1.
If the types do not match, and no type conversion is applicable, the passed argument type is checked to see if it is an n-dimensional array of the formal parameter type. If it is not, the function call is invalid. If it is, we call this a foreach argument.
For all foreach arguments, the number and sizes of dimensions must match. If they do not match, the function call is invalid.
If no foreach argument exists, the function is applied in the normal fashion, and the result has the type specified by the function definition.
The result of the function call expression is an n-dimensional array with the same dimension sizes as the foreach arguments. Each element ei,..,j is the result of applying f to arguments constructed from the original arguments in the following way:
If the argument is not a foreach argument, it is used as-is.
If the argument is a foreach argument, the element at index [i,…,j] is used.
If more than one argument is an array, all of them have to be the same size, and they are traversed in parallel.
[Examples:
This works even if the function is declared to take an array as one of its arguments. If pval is defined as a function that takes one argument that is a vector of Reals and returns a Real, then it can be used with an actual argument which is a two-dimensional array (a vector of vectors). The result type in this case will be a vector of Real.
Add(1, [1,2,3]) adds one to each of the elements of the second argument giving the result [2,3,4]. However, it is illegal to write 1 + [1,2,3], because the rules for the built-in operators are more restrictive.]
An “empty” function call is a call that does not return any results. [An empty call is of limited use in Modelica since a function call without results does not contribute to the simulation, and is not allowed to have side-effects that influence the simulation state.]
An empty call can occur either as a kind of “null” equation or “null” statement, [e.g. as in the empty calls to eigen() in the example below:
]
There are basically four groups of built-in functions in Modelica:
Intrinsic mathematical and conversion functions, see section 3.7.1.
Derivative and special operators with function syntax, see section 3.7.2.
Event-related operators with function syntax, see section 3.7.3.
Built-in array functions, see section 10.3.
Note that when the specification references a function having the name of a built-in function it references the built-in function, not a user-defined function having the same name.
Whenever a record is defined, a record constructor function with the same name and in the same scope as the record class is implicitly defined according to the following rules:
The declaration of the record is partially flattened including inheritance, modifications, redeclarations, and expansion of all names referring to declarations outside of the scope of the record to their fully qualified names [in order to remove potentially conflicting import statements in the record constructor function due to flattening the inheritance tree].
All record elements [i.e., components and local class definitions] of the partially flattened record declaration are used as declarations in the record constructor function with the following exceptions:
Component declarations which do not allow a modification [such as constant Real c=1 or final parameter Real] are declared as protected components in the record constructor function.
Prefixes (constant, parameter, final, discrete,…) of the remaining record components are removed.
The prefix input is added to the public components of the record constructor function.
An instance of the record is declared as output parameter [using a name, not appearing in the record] together with a modification. In the modification, all input parameters are used to set the corresponding record variables.
A record constructor can only be called if the referenced record class is found in the global scope, and thus cannot be modified.
[This allows to construct an instance of a record, with an optional modification, at all places where a function call is allowed. Examples:
In the following example, a convenient data sheet library of components is built up:
Example showing most of the situations, which may occur for the implicit record constructor function creation. With the following record definitions
the following record constructor functions are implicitly defined (the name of the output, given in italic below, is not defined; it should be chosen to not cause any conflict)
and can be applied in the following way
The above example is only used to show the different variants appearing with prefixes, but it is not very meaningful, because it is simpler to just use a direct modifier.
]
A constructor of a record R can be used to cast an instance m of a model, block, connector class M to a value of type R, provided that for each component defined in R (that do not have a default value) there is also a public component defined in M with identical name and type. A nested record component of R is handled as follows, if the corresponding component of M is a model/block/connector a nested record constructor is called - otherwise the component is used directly; and the resulting call/component is used as argument to the record constructor R. If the corresponding component of R in M is a conditional component, it is an error. [The corresponding binding would be illegal since not a connect-statement.] The instance m is given as single (un-named) argument to the record constructor of R. The interpretation is that R(m) is replaced by a record constructor of type R where all public components of M that are present in R are assigned to the corresponding components of R. The record cast can be used in vectorized form according to section 12.4.6. [Note, this cast operation is uniquely distinguished from a record constructor call, because an argument of the record constructor cannot be a model, block or connector instance.]
[Example:
]
Derivatives of functions can be declared explicitly using the derivative annotation, see section 12.7.1, whereas a function can be defined as a partial derivative of another function using the der-operator in a short function definition, see section 12.7.2.
A function declaration can have an annotation derivative specifying the derivative function or preferably, for a function written in Modelica, use the smoothOrder annotation to indicate that the tool can construct the derivative function automatically, section 18.3. The derivative annotation can influence simulation time and accuracy and can be applied to both functions written in Modelica and to external functions. A derivative annotation can state that it is only valid under certain restrictions on the input arguments. These restrictions are defined using the following optional attributes: order (only a restriction if order>1, the default for order is 1), noDerivative, and zeroDerivative. The given derivative-function can only be used to compute the derivative of a function call if these restrictions are satisfied. There may be multiple restrictions on the derivative, in which case they must all be satisfied. The restrictions also imply that some derivatives of some inputs are excluded from the call of the derivative (since they are not necessary). A function may supply multiple derivative functions subject to different restrictions, the first one that can be used (i.e. satisfying the restrictions) will be used for each call. [This means that the most restrictive derivatives should be written first.]
[Example:
]
The inputs to the derivative function of order 1 are constructed as follows:
First are all inputs to the original function, and after all them we will in order append one derivative for each input containing reals. These common inputs must have the same name, type, and declaration order for the function and its derivative.
The outputs are constructed by starting with an empty list and then in order appending one derivative for each output containing reals. The outputs must have the same type and declaration order for the function and its derivative.
If the Modelica function call is a nth derivative (n>=1), i.e. this function call has been derived from an (n-1)th derivative by differentiation inside the tool, an annotation(order=n+1)=..., specifies the (n+1)th derivative, and the (n+1)th derivative call is constructed as follows:
The input arguments are appended with the (n+1)th derivative, which are constructed in order from the nth order derivatives.
The output arguments are similar to the output argument for the nth derivative, but each output is one higher in derivative order. The outputs must have the same type and declaration order for the function and its derivative.
[The restriction that only the result of differentiation can use higher-order derivatives ensures that the derivatives x, der_x, … are in fact derivatives of each other. Without that restriction we would have both der(x) and x_der as inputs (or perform advanced tests to verify that they are the same).]
[Example: Given the declarations
the equation
(…,y(t),…)=foo0(…,x(t),b,…);
implies that:
(…,d y(t)/dt,…)=foo1(…,x(t),b,…, …,d x(t)/dt,…);
(…,d^2 y(t)/dt^2,…)=foo2(…,x(t),b,…,d x(t)/dt,…, …,d^2 x(t)/dt^2,…);
]
An input or output to the function may be any simple type (Real, Boolean, Integer, String and enumeration types) or a record. For a record containing Reals the corresponding derivative uses a derivative record, that only contain the real-predefined types and sub-records containing reals (handled recursively) from the original record. When using smoothOrder, then the derivative record is automatically constructed. The function must have at least one input containing reals. The output list of the derivative function may not be empty.
[Here is one example use case with records mixing Reals and non-Reals as inputs and outputs
]
zeroDerivative=inputVar1 \{, zeroDerivative=inputVar2 \}
The derivative function is only valid if inputVar1 (and inputVar2 etc.) are independent of the variables the function call is differentiated with respect to (i.e. that the derivative of inputVar1 is “zero”). The derivative of inputVar1 (and inputVar2 etc.) are excluded from the argument list of the derivative-function. If the derivative-function also specifies a derivative the common variables should have consistent zeroDerivative.
[Assume that function f takes a matrix and a scalar. Since the matrix argument is usually a parameter expression it is then useful to define the function as follows (the additional derivative = f_general_der is optional and can be used when the derivative of the matrix or offset is non-zero). Note that f_der must have zeroDerivative for both y and offset, but f_general_der may not have zeroDerivative for either of them (it may zeroDerivative for x_der, y_der, or offset_der).
]
noDerivative=inputVar1
The derivative of inputVar1 is excluded from the argument list of the derivative-function. This relies on assumptions on the arguments to the function; and the function should document these assumptions (it is not always straightforward to verify them). In many cases even the undifferentiated function will only behave correctly under these assumptions.
The inputs excluded using zeroDerivative or noDerivative may be of any type (including types not containing reals).
[Assume that function fg is defined as a composition f(x, g(x)). When differentiating f it is useful to give the derivative under the assumption that the second argument is defined in this way:
This is useful if g represents the major computational effort of fg.]
A class defined as:
is the partial derivative of a function, and may only be used as declarations of functions.
The semantics is that a function [and only a function] can be specified in this form, defining that it is the partial derivative of the function to the right of the equal sign (looked up in the same way as a short class definition - the looked up name must be a function), and partially differentiated with respect to each IDENT in order (starting from the first one). The IDENT must be Real inputs to the function.
The comment allows a user to comment the function (in the info-layer and as one-line description, and as icon).
[Example: The specific enthalpy can be computed from a Gibbs-function as follows:
]
Every function with one output formal parameter may have one or more “inverse” annotations to define inverses of this function:
The meaning is that function ”” is one inverse to function ”” where the previous output ”y” is now an input and the previous input ”” is now an output. More than one inverse can be defined within the same inverse annotation. Several inverses are separated by commas. [The inverse requires that for all valid values of the input arguments of (…,y, …) and being calculated as := (…, y, …) implies the equality y = (…, , …,) up to a certain precision.]
Function ”” can have any number and types of formal parameters with and without default value. The restriction is that the “number of unknown variables” (see section 4.7) in the output formal parameter of both ”” and ”” must be the same and that ”” must have exactly the same formal parameters as ”” (with the same defaults, if a formal parameter ”” has a default), but the order of the formal parameters may be permuted.
[Example:
]
Here, the word function is used to refer to an arbitrary external routine, whether or not the routine has a return value or returns its result via output parameters (or both). The Modelica external function call interface provides the following:
Support for external functions written in C (specifically C89) and FORTRAN 77. Other languages, e.g. C++ and Fortran 90, may be supported in the future, and provided the function is link-compatible with C89 or FORTRAN 77 it can be written in any language.
Mapping of argument types from Modelica to the target language and back.
Natural type conversion rules in the sense that there is a mapping from Modelica to standard libraries of the target language.
Handling arbitrary parameter order for the external function.
Passing arrays to and from external functions where the dimension sizes are passed as explicit integer parameters.
Handling of external function parameters which are used both for input and output, by passing an output that has a declaration assignment to the external function. [Declaration assignments are executed prior to calling the external function.]
The format of an external function declaration is as follows.
Components in the public part of an external function declaration shall be declared either as input or output. [This is just as for any other function. The components in the protected part allow local variables for temporary storage to be declared.]
The language-specification must currently be one of "builtin", "C", "C..." (for one of the specific C-standards like C89, C99, and C11 – which specifies that it relies on the C standard library of that version) or "FORTRAN 77". Unless the external language is specified, it is assumed to be "C". [The intended use of e.g. C99 is to detect if the user tries to link with a C99-function using a C89 compiler.]
The "builtin" specification is only used for functions that are defined to be built-in in Modelica. The external-function call mechanism for "builtin" functions is implementation-defined. [Typically, for functions from the standard C-library, the prototype of the function is provided but no library annotation. Currently, there are no other builtin functions defined in Modelica.]
[Example:
]
The external-function-call specification allows functions whose prototypes do not match the default assumptions as defined below to be called. It also gives the name used to call the external function. If the external call is not given explicitly, this name is assumed to be the same as the Modelica name.
The only permissible kinds of expressions in the argument list are component references, scalar constants, and the function size applied to an array and a constant dimension number. The annotations are used to pass additional information to the compiler when necessary.
A component reference to a component that is part of an input or output is treated the same way as a top-level input or output in the external call.
The arguments of the external function are declared in the same order as in the Modelica declaration, unless specified otherwise in an explicit external function call. Protected variables (i.e. temporaries) are passed in the same way as outputs, whereas constants and size-expression are passed as inputs.
Arguments of simple types are by default mapped as follows for C:
Modelica | C | |
---|---|---|
Input | Output | |
Real | double | double * |
Integer | int | int * |
Boolean | int | int * |
String | const char * | const char ** |
Enumeration type | int | int * |
An exception is made when the argument is of the form size(…, …). In this case the corresponding C-type is size_t.
Strings are NUL-terminated (i.e., terminated by ’\’) to facilitate calling of C functions. When returning a non-literal string, the memory for this string must be allocated with function ModelicaAllocateString (see section 12.9.6) [It is not suitable to use malloc, because a Modelica simulation environment may have its own allocation scheme, e.g., a special stack for local variables of a function]. After return of the external function, the Modelica environment is responsible for the memory allocated with ModelicaAllocateString (e.g., to free this memory, when appropriate). It is not allowed to access memory that was allocated with ModelicaAllocateString in a previous call of this external function. [Memory that is not passed to the Modelica simulation environment, such as memory that is freed before leaving the function, or in an ExternalObject, see section 12.9.7, should be allocated with the standard C-mechanisms, like calloc(..)].
Boolean values are mapped to C such that false in Modelica is 0 in C and true in Modelica is 1 in C. [However, the C-function should interpret any non-zero value as true.] If the returned value from C is zero it is treated as false in Modelica; otherwise as true.
Arguments of simple types are by default mapped as follows for FORTRAN 77:
Modelica | FORTRAN 77 | |
---|---|---|
Input | Output | |
Real | DOUBLE PRECISION | DOUBLE PRECISION |
Integer | INTEGER | INTEGER |
Boolean | LOGICAL | LOGICAL |
Enumeration type | INTEGER | INTEGER |
String | Special | Not Available |
Sending string literals to FORTRAN 77 subroutines/functions is supported for Lapack/Blas-routines, and the strings are NUL-terminated for compatibility with C. Returning strings from FORTRAN 77 subroutines/functions is currently not supported.
Enumeration types used as arguments are mapped to type int when calling an external C function, and to type INTEGER when calling an external FORTRAN function. The i:th enumeration literal is mapped to integer value i, starting at one.
Return values are mapped to enumeration types analogously: integer value 1 is mapped to the first enumeration literal, 2 to the second, etc. Returning a value which does not map to an existing enumeration literal for the specified enumeration type is an error.
Unless an explicit function call is present in the external declaration, an array is passed by its address followed by n arguments of type size_t with the corresponding array dimension sizes, where n is the number of dimensions. [The type size_t is a C unsigned integer type.]
Arrays are by default stored in row-major order when calling C functions and in column-major order when calling FORTRAN 77 functions. These defaults can be overridden by the array layout annotation. See the example below.
The table below shows the mapping of an array argument in the absence of an explicit external function call when calling a C function. The type T is allowed to be any of the simple types which can be passed to C as defined in section 12.9.1.1 or a record type as defined in section 12.9.1.3 and it is mapped to the type T’’ as defined in these sections for input arguments.
Modelica | C |
---|---|
Input and Output | |
T[] | T’ *, size_t |
T[,] | T’ *, size_t , size_t |
T[, …, ] | T’ *, size_t , …, size_t |
The method used to pass array arguments to FORTRAN 77 functions in the absence of an explicit external function call is similar to the one defined above for C: first the address of the array, then the dimension sizes as integers. See the table below. The type T is allowed to be any of the simple types which can be passed to FORTRAN 77 as defined in section 12.9.1.1 and it is mapped to the type T’ as defined in that section.
Modelica | FORTRAN 77 |
---|---|
Input and Output | |
T[] | T’, INTEGER |
T[,] | T’, INTEGER , INTEGER |
T[, …, ] | T’, INTEGER , …, INTEGER |
[The following two examples illustrate the default mapping of array arguments to external C and FORTRAN 77 functions.
The corresponding C prototype is as follows:
If the external function is written in FORTRAN 77, i.e.:
the default assumptions correspond to a FORTRAN 77 function defined as follows:
]
When an explicit call to the external function is present, the array and the sizes of its dimensions must be passed explicitly.
[This example shows how arrays can be passed explicitly to an external FORTRAN 77 function when the default assumptions are unsuitable.
The corresponding FORTRAN 77 subroutine would be declared as follows:
This example shows how to pass an array in column major order to a C function.
This corresponds to the following C-prototype:
double fie(double *, size_t, size_t);
]
Mapping of record types is only supported for C. A Modelica record class that contains simple types, other record elements, is mapped as follows:
The record class is represented by a struct in C.
Each element of the Modelica record is mapped to its corresponding C representation.
The elements of the Modelica record class are declared in the same order in the C struct.
Arrays cannot be mapped.
Records are passed by reference (i.e. a pointer to the record is being passed).
[For example:
]
If there is a single output parameter and no explicit call of the external function, or if there is an explicit external call in the form of an equation, in which case the LHS must be one of the output parameters, the external routine is assumed to be a value-returning function. Mapping of the return type of functions is performed as indicated in the table below. Storage for arrays as return values is allocated by the calling routine, so the dimensions of the returned array are fixed at call time. Otherwise the external function is assumed not to return anything; i.e., it is really a procedure or, in C, a void-function. [In this case, argument type mapping according to section 12.9.1.1 is performed in the absence of any explicit external function call.]
Return types are by default mapped as follows for C and FORTRAN 77:
Modelica | C | FORTRAN 77 |
---|---|---|
Real | double | DOUBLE PRECISION |
Integer | int | INTEGER |
Boolean | int | LOGICAL |
String | const char* | Not allowed. |
T[, …, ] | Not allowed. | Not allowed. |
Enumeration type | int | INTEGER |
Record | See section 12.9.1.3. | Not allowed. |
The element type T of an array can be any simple type as defined in section 12.9.1.1 or, for C, a record type is returned as a value of the record type defined in section 12.9.1.3.
Any potential aliasing in the external function is the responsibility of the tool and not the user. An external function is not allowed to internally change the inputs (even if they are restored before the end of the function).
[Example:
The following Modelica function:
can on most systems be transformed into the following C function:
The reason for not allowing the external function to change the inputs is to ensure that inputs can be stored in static memory and to avoid superfluous copying (especially of matrices). If the routine does not satisfy the requirements the interface must copy the input argument to a temporary. This is rare but occurs e.g. in dormlq in some Lapack implementations. In those special cases the writer of the external interface have to copy the input to a temporary. If the first input was changed internally in myfoo the designer of the interface would have to change the interface function “foo” to:
Note that we discuss input arguments for Fortran-routines even though FORTRAN 77 does not formally have input arguments and forbid aliasing between any pair of arguments to a function (Section 15.9.3.6 of X3J3/90.4). For the few (if any) FORTRAN 77 compilers that strictly follow the standard and are unable to handle aliasing between input variables the tool must transform the first call of foo into
The use of the function foo in Modelica is uninfluenced by these considerations.
]
The following annotations are useful in the context of calling external functions from Modelica, and they should occur on the external clause and no other standard annotations should occur on the external-clause. They can all specify either a scalar value or an array of values as indicated below for annotation (Library=…):
The annotation(Library="libraryName"), used by the linker to include the library file where the compiled external function is available.
The annotation(Library=("libraryName1","libraryName2")), used by the linker to include the library files where the compiled external function is available and additional libraries used to implement it. For shared libraries it is recommended to include all non-system libraries in this list.
The annotation(Include="includeDirective"), used to include source files, [e.g., header files or source files that contain the functions referenced in the external function declaration], needed for calling the external function in the code generated by the Modelica compiler. The included code should be valid C89 code.
The annotation(IncludeDirectory="modelica://LibraryName/Resources/Include"), used to specify a location for header files. The preceding one is the default and need not be specified; but another location could be specified by using an URI name for the include directory, see section 13.2.3.
The annotation(LibraryDirectory="modelica://LibraryName/Resources/Library"), used to specify a location for library files. The preceding one is the default and need not be specified; but another location could be specified by using an URI name for the library directory, see section 13.2.3. Different versions of one object library can be provided [e.g. for Windows and for Linux] by providing a “platform” directory below the “LibraryDirectory”. If no “platform” directory is present, the object library must be present in the “LibraryDirectory”. The following “platform” names are standardized:
win32 [Microsoft Windows 32 bit]
win64 [Microsoft Windows 64 bit]
linux32 [Linux Intel 32 bit]
linux64 [Linux Intel 64 bit]
The ”win32”/”win64” directories may contain ”gcc47”, ”vs2010”, ”vs2012” for specific versions of these compilers and these are used instead of the general ”win32”/”win64” directories, and similarly for other platforms.
If the directory for the specific compiler version is missing the platform specific directory is used. [A tool may give diagnostics if the directory corresponding to the selected compiler version is missing. The directories may use symbolic links - or use a text-file as described below: e.g. a text-file ”vs2008” containing the text ”../win32/vs2005” (or ”vs2005”) suggesting that it is compatible with vs2005.]
The LibraryName used for IncludeDirectory and LibraryDirectory indicates the top-level class where the annotation is found in the Modelica source code.
[Example: to show the use of external functions and of object libraries:
Directory structure:
Note that calling MyExternalFunctions.ExternalFunc1 will use header and library files from ExternalFunctions.
Header file for the function in the dynamic link / shared library ExternalLib2 so that the desired functions are defined to be exported for Microsoft VisualStudio and for GNU C-compiler (note, for Linux it is recommended to use the compiler option “-fPIC” to build shared libraries or object libraries that are later transformed to a shared library):
]
The Library name and the LibraryDirectory name in the function annotation are mapped to a linkage directive in a compiler-dependent way thereby selecting the object library suited for the respective computer platform.
[Here all parameters to the external function are input parameters. One function value is returned. If the external language is not specified, the default is "C", as below.
This corresponds to the following C-prototype:
[In the following example, the external function call is given explicitly which allows passing the arguments in a different order than in the Modelica version.
This corresponds to the following C-prototype:
Example call in Modelica:
Translated call in C:
]
[The following external function returns two results: one function value and one output parameter value. Both are mapped to Modelica output parameters.
This corresponds to the following C-prototype:
Example call in Modelica:
Translated call in C:
]
The following utility functions can be called in external Modelica functions written in C. These functions are defined in file ModelicaUtilities.h:
The following functions produce a message in different ways. The Message-functions only produce the message, but the Warning- and Error-functions combine this with error handling as follows.
The Warning-functions view the message as a warning and can skip duplicated messages similarly as an assert with level=AssertionLevel.Warning in the Modelica code.
The Error-functions never return to the calling function, but handle the error similarly to an assert with level=AssertionLevel.Error in the Modelica code.
ModelicaMessage ModelicaWarning ModelicaError |
void Modelica{Message,Warning,Error}(const char* string) Output the message string (no format control). |
ModelicaFormatMessage ModelicaFormatWarning ModelicaFormatError |
void ModelicaFormat{Message,Warning,Error}(const char* string,…) Output the message under the same format control as the C-function printf. |
ModelicaVFormatMessage ModelicaVFormatWarning ModelicaVFormatError |
void ModelicaVFormat{Message,Warning,Error}(const char*string, va_list) Output the message under the same format control as the C-function vprintf. |
And then the string handling functions:
ModelicaAllocateString |
|
||
ModelicaAllocateStringWithErrorReturn |
|
External functions may have internal memory reported between function calls. Within Modelica this memory is defined as instance of the predefined class ExternalObject according to the following rules:
There is a predefined partial class ExternalObject [since the class is partial, it is not possible to define an instance of this class[.
An external object class shall be directly extended from ExternalObject, shall have exactly two function definitions, called ”constructor” and ”destructor”, and shall not contain other elements. The functions ”constructor” and ”destructor” shall not be replaceable.
The constructor function is called exactly once before the first use of the object. For each completely constructed object, the destructor is called exactly once, after the last use of the object, even if an error occurs. The constructor shall have exactly one output argument in which the constructed instance derived from ExternalObject is returned. The destructor shall have no output arguments and the only input argument of the destructor shall be of the type derived from ExternalObject. It is not legal to call explicitly the constructor and destructor functions. The constructor shall initialize the object, and must not require any other calls to be made for the initialization to be complete (e.g., from an initial algorithm or initial equation). The destructor shall delete the object, and must not require any other calls to be made for the deletion to be complete (e.g., from a ’when terminal()’ clause). The constructor may not assume that pointers sent to the external object will remain valid for the life-time of the external object. [An exception is that if the pointer to another external object is given as argument to the constructor, that pointer will remain valid as long as the other external object lives.]
External objects may be a protected component (or part of one) in a function. The constructor is in that case called at the start of the function call, and the destructor when the function returns, or when recovering from errors in the function.
External objects may be an input (or part of an input) to a function, in that case the destructor is not called (since the external object is active before and after the function call). Normally this is an external function, but it could be a non-external function as well (e.g. calling external functions one or more times). The function input may not have a default value using the constructor.
Classes derived from ExternalObject can neither be used in an extends-clause nor in a short class definition.
Only the constructor may return external objects and external object can only be bound in component declarations and neither modified later nor assigned to.
No function may return a component containing an external object (since only the constructor may return an external object and the constructor exactly returns the external object).
External functions may be defined which operate on the internal memory of an ExternalObject. An ExternalObject used as input argument or return value of an external C-function is mapped to the C-type ”void*”.
[Example:
A user-defined table may be defined in the following way as an ExternalObject
(the table is read in a user-defined format from file and has memory for the last used table interval):
and used in the following way:
This requires to provide the following Modelica function:
The external C-functions may be defined in the following way:
]