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 binding equations. Comment strings and annotations can be given for any formal parameter declaration, as usual in Modelica declarations.
[Explicit default values are shown for the third input parameter and the second output parameter in the example above.]
[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 that 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:
Only input and output formal parameters are allowed in the function’s public variable section.
Input formal parameters are read-only after being bound to the actual arguments or default values, i.e., they shall not be assigned values in the body of the function.
A function shall not be used in connections, shall not have equations, shall 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 specialized classes type, record, operator record, and function; and it must not contain, e.g., model, block, operator or connector components.
A function may not contain components of type Clock.
The elements of a function shall 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 shall not be partial, and the output variables must be assigned inside the function either in binding equations 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.
If a function is declared as impure any function extending from it shall be declared as 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.
Initial equations and initial algorithms.
Binding equations for components declared as parameter – which is seen as syntactic sugar for having a parameter with fixed=false and the binding as an initial equation.
[Thus, evaluation of the same function call at a later time during simulation is not guaranteed to result in the same value as when the parameter was initialized, seemingly breaking the declaration equation.]
Binding equations for 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.]
By section 6.6, it follows that an impure function can only be passed as argument to a function formal parameter of impure type. A function having a formal function parameter that is 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.]
[Example:
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 – a single call used for both the two elements of the matrix, as well as 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 positional arguments, they are placed in the first 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. The value of the argument is placed in the slot, filling it (it is an error if this slot is already filled). 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 shall not depend on non-input variables in the function. The list of filled slots is used as the argument list for the call (it is an error if any unfilled slots still remain).
Special purpose operators with function syntax defined in the specification shall 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 coercion, section 10.6.13, 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 type-specifier of a partial function that has no replaceable elements. It cannot be the type-specifier 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 (i.e., a formal parameter of function type of the enclosing function),
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-compatible (definition 6.8) with 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 (definition 6.8) with the same function where all bound arguments are removed.
[Thus, for checking function type compatibility, bound formal parameters are ignored.]
[Example: Function partial application as argument, positional argument passing, according to case (b) above:
Call with function partial application as named input argument:
]
[Example: 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: 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 binding equation (= expression) with an expression may be present for a component.
[Declaration assignments of the form := expression are deprecated, but otherwise identical to binding equations.]
A binding equation 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 binding equations has been executed; it is an error if no such order exists (i.e. the binding must be acyclic).
Binding equations can only be used for components of a function. If no binding equation 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. Binding equations for input formal parameters are interpreted as default arguments, as described in section 12.4.1.
[It is recommended to check for use of uninitialized variables statically – if this is not possible a warning is recommended combined with a run-time check.]
[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 binding equation, 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.3.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. Potential vectorization of this call is defined as follows. 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 -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 -dimensional array e with the same dimension sizes as the foreach arguments. Each element e[, , ] 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 [, , ] 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.
[Example:
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 Real vector 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, but it is useful to check assertions and in certain cases for desired side-effects, see section 12.3.]
An empty call can occur either as a kind of “null equation” or “null statement”.
[Example: The empty calls to eigen() are examples of a “null equation” and a “null statement”:
]
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.4.
Event-related operators with function syntax, see section 3.7.5.
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.
[The partial flattening is performed in order to remove potentially conflicting import-clauses 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 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 constructing 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 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.
[The problem if R would be a conditional component is that the corresponding binding would be illegal since it is not a connect-statement.]
[The record 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
implies that: | ||||
]
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 Real values, the corresponding derivative uses a derivative record that only contains 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 shall not be empty.
[Example: Here is one example use case with records mixing Real and non-Real 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 shall 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, and 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 should have a union of output and formal parameters that is the same or a sub-set of that union for , but the order of the formal parameters may be permuted.
[Example: Same union of variables:
]
The sub-set case is useful if computes the inverse of within a region, or up to a certain tolerance. Then, may specify as inverse with fewer arguments, skipping the arguments for tolerance and/or the region.
[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 binding equation to the external function.
[Binding equations 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" (deprecated), "C", "C..." (for one of the specific C standards like C89, C99, and C11 – specifying 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 deprecated "builtin" specification is only used for the elementary mathematical functions described in section 3.7.3. 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.
If the function has annotation(Include="includeDirective"), section 12.9.4 it is assumed that it contains an actual prototype and no prototype shall be automatically generated. In that case the input argument pointers shall be const pointers (indicating that they do not modify the inputs), and non-const pointers are deprecated. The optional external-function-call is still used for determining the name of the function, and order of arguments, as described below.
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 ’\0’) to facilitate calling of C functions. When returning a non-literal string, see section 12.9.6.2 for details on memory allocation.
Boolean values are mapped to C such that false in Modelica is 0 in C and true in Modelica is 1 in C. If the returned value from C is 0 it is treated as false in Modelica; otherwise as true.
[It is recommended that the C function should interpret any non-zero value 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 |
String | Special | Not available |
Enumeration type | INTEGER | INTEGER |
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 :th enumeration literal is mapped to integer value , starting at 1.
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-clause, an array is passed by its address followed by arguments of type size_t with the corresponding array dimension sizes, where 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 arrayLayout 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 as defined in these sections for input arguments. Array inputs to C-functions are const-pointers, indicating that the arrays shall not be changed.
Modelica | C | |
---|---|---|
Input | Output | |
T[] | const *, size_t | *, size_t |
T[, ] | const *, size_t , size_t | *, size_t , size_t |
T[, ] | const *, , 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 as defined in that section.
Modelica | FORTRAN 77 |
---|---|
Input and output | |
T[] | , INTEGER |
T[, ] | , INTEGER , INTEGER |
T[, , ] | , INTEGER , , INTEGER |
[Example: 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.
[Example: 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.
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).
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 the case of an external function not returning anything, 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 the Library annotation:
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 needed for calling the external function in the code generated by the Modelica compiler. The included code should be valid C89 code.
[Examples of files that can be included are header files or source files that contain the functions referenced in the external function declaration.]
The annotation(IncludeDirectory="modelica://ModelicaLibraryName/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.5.
The annotation(LibraryDirectory="modelica://ModelicaLibraryName/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.5. 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 annotation(SourceDirectory="modelica://ModelicaLibraryName/Resources/Source"), gives the location for source 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 source directory, see section 13.5. It is not specified how they are built.
The win32 or win64 directories may contain gcc47, vs2010, vs2012 for specific versions of these compilers and these are used instead of the general win32 or win64 directories, and similarly for other platforms.
The library on Windows may refer to a lib-file (static library), both a lib- and dll-file (in this case the lib-file is an import-library), or just a dll-file. It shall not refer to an obj-file.
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 ModelicaLibraryName used for IncludeDirectory, LibraryDirectory, and SourceDirectory indicates the top-level class where the annotation is found in the Modelica source code.
[Example: Use of external functions and of object libraries:
Directory structure:
Note that calling MyExternalFunctions.ExternalFunc1 will use header and library files from ExternalFunction, the ExternalFunctions.Example will not use ExternalFunc3.c, and one library file may contain multiple functions.
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.
[Example: 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:
[Example: 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:
]
[Example: 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:
]
This section describes the utility functions declared in ModelicaUtilities.h, which can be called in external Modelica functions written in C.
The functions listed below produce a message in different ways.
Expression | Description | Details |
---|---|---|
ModelicaMessage() | Message with fixed string | Function 12.1 |
ModelicaWarning() | Warning with fixed string | |
ModelicaError() | Error with fixed string | |
ModelicaFormatMessage(, ) | Message with printf style formatting | Function 12.2 |
ModelicaFormatWarning(, ) | Warning with printf style formatting | |
ModelicaFormatError(, ) | Error with printf style formatting | |
ModelicaVFormatMessage(, ) | Message with vprintf style formatting | Function 12.3 |
ModelicaVFormatWarning(, ) | Warning with vprintf style formatting | |
ModelicaVFormatError(, ) | Error with vprintf style formatting |
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.
Output the fixed message string (no format control).
Output the message under the same format control as the C function printf.
Output the message under the same format control as the C function vprintf.
The functions listed below are related to string allocation.
Expression | Description | Details |
---|---|---|
ModelicaAllocateString() | Allocate or error | Function 12.4 |
ModelicaAllocateStringWithErrorReturn() | Allocate or null | Function 12.5 |
ModelicaDuplicateString() | Duplicate or error | Function 12.6 |
ModelicaDuplicateStringWithErrorReturn() | Duplicate or null | Function 12.7 |
Allocate memory for a writeable non-literal string which is used as a return argument of an external Modelica function. It allocates characters and the last one is set to nul. If an error occurs, this function does not return, but calls ModelicaError.
Same as ModelicaAllocateString, except that in case of error, the function returns 0. This allows the external function to close files and free other open resources in case of error. After cleaning up resources, use ModelicaError or ModelicaFormatError to signal the error.
Returns a writeable duplicate of the nul-terminated string . If an error occurs, this function does not return, but calls ModelicaError.
Same as ModelicaDuplicateString, except that in case of error, the function returns 0. This allows the external function to close files and free other open resources in case of error. After cleaning up resources, use ModelicaError or ModelicaFormatError to signal the error.
The valid return values for an external function returning a String are:
A literal String.
A string given as String input to the external function.
A string pointer returned by one the functions in the table above.
Thus if an external function wants to create a non-literal string it must be allocated with one of the functions in this section, e.g., ModelicaAllocateString. 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.]
[The reason why one should avoid, for instance, malloc for string allocation is that a Modelica simulation environment may have its own allocation scheme, e.g., a special stack for local variables of a function.]
External functions may need to store their internal memory 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 shall 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 shall not have a default value using the constructor.]
An external object class shall be of the specialized class class.
[This is the only use of class.]
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 an external object can only be bound in component declarations and neither modified later nor assigned to.
[It follows that a function cannot 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:
]