diff --git a/docs/0___preface.adoc b/docs/0___preface.adoc new file mode 100644 index 000000000..72a533e23 --- /dev/null +++ b/docs/0___preface.adoc @@ -0,0 +1,53 @@ +*Abstract* + +This document defines the Modelica{empty}footnote:[Modelica is a registered trademark of the Modelica Association.] +language, version {revnumber}, which is developed by the Modelica Association, a non-profit organization with seat in Linköping, Sweden. +Modelica is a freely available, object-oriented language for modeling of large, complex, and heterogeneous systems. +It is suited for multi-domain modeling, for example, mechatronic models in robotics, automotive and aerospace applications involving mechanical, electrical, hydraulic control and state machine subsystems, process oriented applications and generation and distribution of electric power. +Models in Modelica are mathematically described by differential, algebraic and discrete equations. +No particular variable needs to be solved for manually. +A Modelica tool will have enough information to decide that automatically. +Modelica is designed such that available, specialized algorithms can be utilized to enable efficient handling of large models having more than one hundred thousand equations. +Modelica is suited and used for hardware-in-the-loop simulations and for embedded control systems. +More information is available at https://modelica.org[]. + +Copyright © 1998-2023, Modelica Association (https://modelica.org[]) + +All rights reserved. +Reproduction or use of editorial or pictorial content is permitted, i.e., this document can be freely distributed especially electronically, provided the copyright notice and these conditions are retained. +No patent liability is assumed with respect to the use of information contained herein. +While every precaution has been taken in the preparation of this document no responsibility for errors or omissions is assumed. + +The contributors to this and to previous versions of this document are listed in <>. + +[preface] +== Preface + +Modelica is a freely available, object-oriented language for modeling of large, complex, and heterogeneous physical systems. +From a user's point of view, models are described by schematics, also called object diagrams. +Examples are shown below: + +[.text-center] +image::media/diagram_examples.png[width=95%] + +A schematic consists of connected components, like a resistor, or a hydraulic cylinder. +A component has _connectors_ (often also called _ports_) that describe the interaction possibilities, e.g., an electrical pin, a mechanical flange, or an input signal. +By drawing connection lines between connectors a physical system or block diagram model is constructed. +Internally a component is defined by another schematic, or on "bottom" level, by an equation-based description of the model in Modelica syntax. + +The Modelica language is a textual description to define all parts of a model and to structure model components in libraries, called packages. +An appropriate Modelica simulation environment is needed to graphically edit and browse a Modelica model (by interpreting the information defining a Modelica model) and to perform model simulations and other analysis. +Information about such environments is available at https://modelica.org/tools[]. +Basically, all Modelica language elements are mapped to differential, algebraic and discrete equations. +There are no language elements to describe directly partial differential equations, although some types of discretized partial differential equations can be reasonably defined, e.g., based on the finite volume method and there are Modelica libraries to import results of finite-element programs. + +This document defines the details of the Modelica language. +It is not intended to learn the Modelica language with this text. +There are better alternatives, such as the Modelica books referenced at https://modelica.org/publications[]. +This specification is used by computer scientists to implement a Modelica translator and by modelers who want to understand the exact details of a particular language element. + +The text directly under the chapter headings are non-normative introductions to the chapters. + +The Modelica language has been developed since 1996. +This document describes version {revnumber} of the Modelica language. +The revision history is available in <>. \ No newline at end of file diff --git a/docs/10__arrays.adoc b/docs/10__arrays.adoc new file mode 100644 index 000000000..d241dd409 --- /dev/null +++ b/docs/10__arrays.adoc @@ -0,0 +1,1469 @@ +== Arrays +:id: arrays + +An array of the specialized classes `type`, `record`, and `connector` can be regarded as a collection of type compatible values (see <>). +Thus an array of the specialized classes `record` or `connector` may contain scalar values whose elements differ in their dimension sizes, but apart from that they must be of the same type. +Such heterogeneous arrays may only be used completely, sliced as specified, or indexed. +An array of other specialized classes can only be used sliced as specified, or indexed. +Modelica arrays can be multidimensional and are "rectangular", which in the case of matrices means all rows in a matrix have equal length, and all columns have equal length. + +Each array has a certain dimensionality, i.e., number of dimensions. +The degenerate case of a _scalar_ variable is not really an array, but can be regarded as an array with zero dimensions. +_Vectors_ have one dimension, matrices (singular: _matrix_) have two dimensions, etc. + +So-called row vectors and column vectors do not exist in Modelica and cannot be distinguished since vectors have only one dimension. +If distinguishing these is desired, row matrices and column matrices are available, being the corresponding two-dimensional entities. +However, in practice this is seldom needed since the usual matrix arithmetic and linear algebra operations have been defined to give the expected behavior when operating on Modelica vectors and matrices. + +Modelica is a strongly typed language, which also applies to array types. +The number of dimensions of an array is fixed and cannot be changed at run-time. +However, the sizes of array dimensions can be computed at run-time. + +The fixed number of array dimensions permits strong type checking and efficient implementation. +The non-fixed sizes of array dimensions on the other hand, allow fairly generic array manipulation code to be written as well as interfacing to standard numeric libraries implemented in other programming languages. + +An array is allocated by declaring an array variable or calling an array constructor. +Elements of an array can be indexed by `Integer`, `Boolean`, or `enumeration` values. + +=== Array Declarations + +The Modelica type system includes scalar number, vector, matrix (number of dimensions, ndim=2), and arrays of more than two dimensions. + +[NOTE] +There is no distinction between a row and column vector. + +The following table shows the two possible forms of declarations and defines the terminology. +`C` is a placeholder for any class, including the built-in type classes `Real`, `Integer`, `Boolean`, `String`, and enumeration types. +The type of a dimension upper bound expression, e.g., n, m, p, ... in the table below, needs to be a subtype of `Integer` or `EB` for a class `EB` that is an enumeration type or subtype of the `Boolean` type. + +Colon (`:`) indicates that the dimension upper bound is unknown and is a subtype of `Integer`. +The size of such a variable can be determined from its binding equation, or the size of any of its array attributes (see also <>). +The size cannot be determined from other equations or algorithms. + +Upper and lower array dimension index bounds are described in <>. + +An array indexed by `Boolean` or enumeration type can only be used in the following ways: + +* Subscripted using expressions of the appropriate type (i.e., `Boolean` or the enumerated type). + +* Binding equations of the form `x1 = x2` are allowed for arrays independent of whether the index types of dimensions are subtypes of `Integer`, `Boolean`, or enumeration types. + +.General forms of declaration of arrays. The notation `EB` stands for an enumeration type or `Boolean`. The general array can have one or more dimensions (latexmath:[k \geq 1]). +[cols="a,a,^a,a,a",options=autowidth] +|=== +|Modelica form 1 |Modelica form 2 |# dims |Designation |Explanation + +|`C x;` |`C x;` |0 |Scalar |Scalar +|`C[n] x;` |`C x[n];` |1 |Vector |n-vector +|`C[EB] x;` |`C x[EB];` |1 |Vector |Vector indexed by `EB` +|`C[n, m] x;` |`C x[n, m];` |2 |Matrix |n × m matrix +|`C[n1, n2, ..., nk] x;` |`C x[n1, n2, ..., nk];` |k |Array |General array +|=== + +A component declared with array dimensions, or where the element type is an array type, is called an _array variable_. +It is a component whose components are _array elements_ (see below). +For an array variable, the ordering of its components matters: +The k-th element in the sequence of components of an array variable `x` is the array element with index `k`, denoted `x[k]`. +All elements of an array have the same type. +An array element may again be an array, i.e., arrays can be nested. +An array element is hence referenced using n indices in general, where n is the number of dimensions of the array. + +A component contained in an array variable is called an _array element_. +An array element has no identifier. +Instead they are referenced by array access expressions called indices that use enumeration values or positive integer index values. + +[example] +==== +Example: The number of dimensions and the dimensions sizes are part of the type, and shall be checked for example at redeclarations. +Declaration form 1 displays clearly the type of an array, whereas declaration form 2 is the traditional way of array declarations in languages such as Fortran, C, C++. + +[source,modelica] +---- +Real[:] v1, v2 // Vectors v1 and v2 have unknown sizes. + // The actual sizes may be different. +---- + +It is possible to mix the two declaration forms although it might be confusing. + +[source,modelica] +---- +Real[3, 2] x[4, 5]; // x has type Real[4, 5, 3, 2]; +---- + +The reason for this order is given by examples such as: + +[source,modelica] +---- +type R3 = Real[3]; +R3 a; +R3 b[1] = {a}; +Real[3] c[1] = b; +---- + +Using a type for `a` and `b` in this way is normal, and substituting a type by its definition allows `c`. + +A vector `y` indexed by enumeration values + +[source,modelica] +---- +type TwoEnums = enumeration(one,two); +Real[TwoEnums] y; +---- +==== + +Zero-valued dimensions are allowed, so: `C x[0];` declares an empty vector, and: `C x[0, 3];` an empty matrix. +Some examples of array dimensions of size one are given in the following table. + +.Special cases of declaration of arrays as 1-vectors, row-vectors, or column-vectors of arrays +[cols="a,a,^a,a,a",options=autowidth] +|=== +|Modelica form 1 |Modelica form 2 |# dims |Designation |Explanation + +|`C[1] x;` |`C x[1];` |1 |Vector |1-vector, representing a scalar +|`C[1, 1] x;` |`C x[1, 1];` |2 |Matrix |(1 × 1)-matrix, representing a scalar +|`C[n, 1] x;` |`C x[n, 1];` |2 |Matrix |(n × 1)-matrix, representing a column +|`C[1, n] x;` |`C x[1, n];` |2 |Matrix |(1 × n)-matrix, representing a row +|=== + +The type of an array of array is the multidimensional array which is constructed by taking the first dimensions from the component declaration and subsequent dimensions from the maximally expanded component type. +A type is maximally expanded, if it is either one of the built-in types (`Real`, `Integer`, `Boolean`, `String`, enumeration type) or it is not a type class. +Before operator overloading is applied, a type class of a variable is maximally expanded. + +[example] +==== +Example: + +[source,modelica] +---- +type Voltage = Real(unit = "V"); +type Current = Real(unit = "A"); +connector Pin + Voltage v; // type class of v = Voltage, type of v = Real + flow Current i; // type class of i = Current, type of i = Real +end Pin; +type MultiPin = Pin[5]; +MultiPin[4] p; // type class of p is MultiPin, type of p is Pin[4, 5]; +type Point = Real[3]; +Point p1[10]; +Real p2[10, 3]; +---- + +The components `p1` and `p2` have identical types. + +[source,modelica] +---- +p2[5] = p1[2] + p2[4]; // equivalent to p2[5, :] = p1[2, :] + p2[4, :] +Real r[3] = p1[2]; // equivalent to r[3] = p1[2, :] +---- +==== + +[NOTE] +-- +Automatic assertions at simulation time: + +Let `A` be a declared array and `i` be the declared maximum dimension size of the di-dimension, then an `assert`-statement `assert(i >= 0, ...)` is generated provided this assertion cannot be checked at compile time. +It is a quality of implementation issue to generate a good error message if the assertion fails. + +Let `A` be a declared array and `i` be an index accessing an index of the di-dimension. +Then for every such index-access an assert statement `+assert(1 <= i and i <= size(A, di), ...)+` is generated, provided this assertion cannot be checked at compile time. + +For efficiency reasons, these implicit `assert`-statements may be optionally suppressed. +-- + +==== Lower and Upper Index Bounds + +The lower and upper index bounds for a dimension of an array indexed by `Integer`, `Boolean`, or `enumeration` values are as follows: + +* An array dimension indexed by `Integer` values has a lower bound of 1 and an upper bound being the size of the dimension. +* An array dimension indexed by `Boolean` values has the lower bound `false` and the upper bound `true`. +* An array dimension indexed by `enumeration` values of the type `E = enumeration(e1, e2, ..., en)` has the lower bound `E.e1` and the upper bound `E.en`. + +=== Flexible Array Sizes + +Regarding flexible array sizes and resizing of arrays in functions, see <>. + +=== Built-in Array Operators and Functions + +Modelica provides a number of built-in functions that are applicable to arrays. + +The `promote` function listed below is utilized to define other array operators and functions. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`promote(A, n)` |Append dimensions of size 1 |<> +|=== + +[[operator:promote,Operator promote]] +Operator promote:: ++ +-- +[source,modelica] +---- +promote(A, n) +---- +Fills dimensions of size 1 from the right to array A up to dimension n, where n >= ndims(A) is required. + +Let `C = promote(A, n)`, with `n~A~ = ndims(A)`, then `+ndims(C) = n+`, `+size(C, j) = size(A, j)+` for `1 ≤ j ≤ n~A+~`, `size(C, j) = 1` for `n~A~ + 1 ≤ j ≤ n, C[i~1~, ..., i~nA~, 1, ..., 1] = A[i~1~, ..., i~nA~]` + +The argument n must be a constant that can be evaluated during translation, as it determines the number of dimensions of the returned array. + +[NOTE] +An n that is not a constant that can be evaluated during translation for `promote` complicates matrix handling as it can change matrix-equations in subtle ways (e.g., changing inner products to matrix multiplication). +-- + +[example] +==== +Some examples of using the functions defined in the following section <> to <>: + +[source,modelica] +---- +Real x[4, 1, 6]; +size(x, 1) = 4; +size(x); // vector with elements 4, 1, 6 +size(2 * x + x) = size(x); +Real[3] v1 = fill(1.0, 3); +Real[3, 1] m = matrix(v1); +Real[3] v2 = vector(m); +Boolean check[3, 4] = fill(true, 3, 4); +---- +==== + +==== Dimension and Size Functions + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`ndims(A)` |Number of dimensions |<> +|`size(A, i)` |Size of single array dimension |<> +|`size(A)` |Sizes of all array dimensions |<> +|=== + +[[operator:ndims,Operator ndims]] +Operator ndims:: ++ +[source,modelica] +---- +ndims(A) +---- ++ +Returns the number of dimensions _k_ of expression _A_, with _k ≥ 0_. + +[[operator:size-of-dim,Operator size]] +Operator size:: ++ +[source,modelica] +---- +size(A, i) +---- +Returns the size of dimension _i_ of array expression _A_ where _1 ≤ i ≤ ndims(A)_. ++ +If A refers to a component of an expandable connector, then the component must be a declared component of the expandable connector, and it must not use colon (`:`) to specify the array size of dimension i. + +[[operator:size-vector,Operator size]] +Operator size:: ++ +[source,modelica] +---- +size(A) +---- ++ +Returns a vector of length `ndims(A)` containing the dimension sizes of _A_. ++ +If _A_ refers to a component of an expandable connector, then the component must be a declared component of the expandable connector, and it must not use colon (`:`) to specify the size of any array dimension. + +==== Dimensionality Conversion Functions + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`scalar(A)` |Extract only element |<> +|`vector(A)` |Vector of all elements |<> +|`matrix(A)` |Two-dimensional array |<> +|=== + +[[operator:scalar,Operator scalar]] +Operator scalar:: ++ +[source,modelica] +---- +scalar(A) +---- ++ +Returns the single element of array _A_. +`size(A, i) = 1` is required for 1 ≤ _i_ ≤ `ndims(A)`. + +[[operator:vector,Operator vector]] +Operator vector:: ++ +[source,modelica] +---- +vector(A) +---- ++ +Returns a 1-vector if _A_ is a scalar, and otherwise returns a vector containing all the elements of the array, provided there is at most one dimension size > 1. + +[[operator:matrix,Operator matrix]] +Operator matrix:: ++ +[source,modelica] +---- +matrix(A) +---- ++ +Returns `promote(A, 2)` if _A_ is a scalar or vector, and otherwise returns the elements of the first two dimensions as a matrix. +`size(A, i)` = 1 is required for 2 < _i_ ≤ `ndims(A)`. + +==== Specialized Array Constructor Functions + +An array constructor function constructs and returns an array computed from its arguments. +Most of the constructor functions listed below construct an array by filling in values according to a certain pattern, in several cases just giving all array elements the same value. +The general array constructor with syntax `array(...)` or `{...}` is described in section <>. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`identity(n)` |Identity matrix |<> +|`diagonal(v)` |Diagonal matrix |<> +|`zeros(n1, n2, n3, ...)` |Array with all elements being 0 |<> +|`ones(n1, n2, n3, ...)` |Array with all elements being 1 |<> +|`fill(s, n1, n2, n3, ...)` |Array with all elements equal |<> +|`linspace(x1, x2, n)` |Vector with equally spaced elements |<> +|=== + +[[operator:identity,Operator identity]] +Operator identity:: ++ +[source,modelica] +---- +identity(n) +---- ++ +Returns the _n × n_ Integer identity matrix, with ones on the diagonal and zeros at the other places. + +[[operator:diagonal,Operator diagonal]] +Operator diagonal:: ++ +[source,modelica] +---- +diagonal(v) +---- ++ +Returns a square matrix with the elements of vector _v_ on the diagonal and all other elements zero. + +[[operator:zeros,Operator zeros]] +Operator zeros:: ++ +[source,subs="+quotes"] +---- +zeros(n~1~, n~2~, n~3~, ...) +---- ++ +Returns the _n~1~ × n~2~ × n~3~ × ..._ Integer array with all elements equal to zero (_n~i~_ ≥ 0). +The function needs one or more arguments, that is, `zeros()` is not legal. + +[[operator:ones,Operator ones]] +Operator ones:: ++ +[source,subs="+quotes"] +---- +ones(n~1~, n~2~, n~3~, ...) +---- ++ +Returns the _n~1~ × n~2~ × n~3~ × ..._ Integer array with all elements equal to one (_n~i~_ ≥ 0). +The function needs one or more arguments, that is, `ones()` is not legal. + +[[operator:fill,Operator fill]] +Operator fill:: ++ +[source,subs="+quotes"] +---- +fill(s, n~1~, n~2~, n~3~, ...) +---- ++ +Returns the _n~1~ × n~2~ × n~3~ × ..._ array with all elements equal to scalar or array expression _s_ (_ni_ >= 0). +The returned array has the same type as _s_. ++ +Recursive definition: `fill(s, n~1~, n~2~, n~3~, ...) = fill(fill(s, n~2~, n~3~, ...), n~1~); fill(s, n) = {s, s, ..., s}`. ++ +The function needs two or more arguments; that is, `fill(s)` is not legal. + +[[operator:linspace,Operator linspace]] +Operator linspace:: ++ +[source,modelica] +---- +linspace(x1, x2, n) +---- ++ +Returns a Real vector with _n_ equally spaced elements, such that `v = linspace(x~1~, x~2~, n)` results in ++ +[latexmath] +++++ +v[i] = x_{1} + (x_{2} - x_{1}) \frac{i - 1}{n - 1} \quad \text{for $1 \leq i \leq n$} +++++ ++ +It is required that n ≥ 2. +The arguments x~1~ and x~2~ shall be numeric scalar expressions. + +==== Reduction Functions and Operators + +The reduction functions listed below "reduce" an array (or several scalars) to one value (normally a scalar, but the `sum` reduction function may give an array as result and also be applied to an operator record). +Note that none of these operators (particularly `min` and `max`) generate events themselves (but arguments could generate events). +The restriction on the type of the input in <> for reduction expressions also applies to the array elements/scalar inputs for the reduction operator with the same name. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`min(A)` |Least element of array |<> +|`min(x, y)` |Least of two scalars |<> +|`min(... for ...)` |Reduction to least value |<> +|`max(A)` |Greatest element of array |<> +|`max(x, y)` |Greatest of two scalars |<> +|`max(... for ...)` |Reduction to greatest value |<> +|`sum(A)` |Sum of scalar array elements |<> +|`sum(... for ...)` |Sum reduction |<> +|`product(A)` |Product of scalar array elements |<> +|`product(... for ...)` |Product reduction |<> +|=== + +[[operator:min-of-array,Operator min]] +Operator min:: ++ +[source,modelica] +---- +min(A) +---- ++ +Returns the least element of array expression `A`; as defined by `<`. + +[[operator:min-binary,Operator min]] +Operator min:: ++ +[source,modelica] +---- +min(x, y) +---- ++ +Returns the least element of the scalars `x` and `y`; as defined by `<`. + +[[operator:min-reduction,Operator min]] +Operator min:: ++ +[source,modelica] +---- +min(e(i, ..., j) for i in u, ..., j in v) +---- ++ +Returns the least value (as defined by `<`) of the scalar expression `e(i, ..., j)` evaluated for all combinations of `i` in `u, ..., j` in `v`. + +[[operator:max-of-array,Operator max]] +Operator max:: ++ +[source,modelica] +---- +max(A) +---- ++ +Returns the greatest element of array expression `A`; as defined by `>`. + +[[operator:max-binary,Operator max]] +Operator max:: ++ +[source,modelica] +---- +max(x, y) +---- ++ +Returns the greatest element of the scalars `x` and `y`; as defined by `>`. + +[[operator:max-reduction,Operator max]] +Operator max:: ++ +[source,modelica] +---- +max(e(i, ..., j) for i in u, ..., j in v) +---- ++ +Returns the greatest value (as defined by `>`) of the scalar `expression e(i, ..., j)` evaluated for all combinations of `i` in `u, ..., j` in `v`. + +[[operator:sum-of-array,Operator sum]] +Operator sum:: ++ +[source,modelica] +---- +sum(A) +---- ++ +Returns the scalar sum of all the elements of array expression `A`. +Equivalent to sum reduction (see below, including application to operator records) over all array indices: `sum(A[j, k, ...] for j, k, ...)` + +[[operator:sum-reduction,Operator sum]] +Operator sum:: ++ +[source,modelica] +---- +sum(e(i, ..., j) for i in u, ..., j in v) +---- ++ +Also described in <>. Returns the sum of the expression `e(i, ..., j)` evaluated for all combinations of `i` in `u, ..., j` in `v`. ++ +The sum reduction function (both variants) may be applied to an operator record, provided that the operator record defines `'0'` and `'+'`. +It is then assumed to form an additive group. ++ +For Integer indexing this is ++ +[source,modelica] +---- +e(u[1], ..., v[1]) + e(u[2], ..., v[1]) + ... + + e(u[end], ..., v[1]) + ... + + e(u[end], ..., v[end]) +---- ++ +For non-`Integer` indexing this uses all valid indices instead of `1..end`. ++ +The type of `sum(e(i, ..., j) for i in u, ..., j in v)` is the same as the type of `e(i, ..., j)`. + +[[operator:product-of-array,Operator product]] +Operator product:: ++ +[source,modelica] +---- +product(A) +---- ++ +Returns the scalar product of all the elements of array expression A. +Equivalent to product reduction (see below) over all array indices: `product(A[j, k, ...] for j, k, ...)` + +[[operator:product-reduction,Operator product]] +Operator product:: ++ +[source,modelica] +---- +product(e(i, ..., j) for i in u, ..., j in v) +---- ++ +Returns the product of the expression `e(i, ..., j)` evaluated for all combinations of `i` in `u, ..., j` in `v`. ++ +For `Integer` indexing this is ++ +[source,modelica] +---- +e(u[1], ..., v[1]) * e(u[2], ..., v[1]) * ... + * e(u[end], ..., v[1]) * ... + * e(u[end], ..., v[end]) +---- ++ +For non-`Integer` indexing this uses all valid indices instead of `1..end`. ++ +The type of `product(e(i, ..., j) for i in u, ..., j in v)` is the same as the type of `e(i, ..., j)`. + +===== Reduction Expressions + +An expression: +[source,modelica] +---- +function-name(expression1 for iterators) +---- +is a _reduction expression_. +The expressions in the iterators of a reduction expression shall be vector expressions. +They are evaluated once for each reduction expression, and are evaluated in the scope immediately enclosing the reduction expression. +If `expression1` contains event-generating expressions, the expressions inside the iterators shall be evaluable. + +For an iterator: +[source,modelica] +---- +IDENT in expression2 +---- +the loop-variable, `IDENT`, is in scope inside `expression1`. +The loop-variable may hide other variables, as in `for`-loops. +The result depends on the `function-name`, and currently the only legal function-names are the built-in operators `array`, `sum`, `product`, `min`, and `max`. +For array, see <>. +If `function-name` is `sum`, `product`, `min`, or `max` the result is of the same type as expression1 and is constructed by evaluating expression1 for each value of the loop-variable and computing the sum, product, min, or max of the computed elements. +For deduction of ranges, see <>; and for using types as ranges see <>. + +.Reduction expressions with iterators +[cols="a,a,a",options=autowidth] +|=== +|Reduction |Restriction on expression1 |Result for empty expression2 + +|`sum` |Integer or Real |`zeros(...)` +|`product` |Scalar Integer or Real |1 +|`min` |Scalar enumeration, Boolean, Integer or Real |Greatest value of type +|`max` |Scalar enumeration, Boolean, Integer or Real |Least value of type +|=== + +The least and greatest values of `Real` are the minimum and maximum representable finite floating point numbers of the underlying type (see also <>). + +[example] +==== +Example: + +[source,modelica] +---- +sum(i for i in 1:10) // Gives sum for i=1..10 of i = 1 + 2 + ... + 10 = 55 +sum(i^2 for i in {1,3,7,6}) // Gives sum for i={1, 3, 7, 6} of i*i = 1 + 9 + 49 + 36 = 95 +{product(j for j in 1:i) for i in 0:4} // Gives {1, 1, 2, 6, 24} +max(i^2 for i in {3,7,6}) // Gives 49 +---- +==== + +==== Matrix and Vector Algebra Functions + +Functions for matrix and vector algebra are listed below. +The function `transpose` can be applied to any matrix. +The functions `outerProduct`, `symmetric`, `cross` and `skew` require Real vector(s) or matrix as input(s) and return a Real vector or matrix. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`transpose(A)` |Matrix transpose |<> +|`outerProduct(x, y)` |Vector outer product |<> +|`symmetric(A)` |Symmetric matrix, keeping upper part |<> +|`cross(x, y)` |Cross product |<> +|`skew(x)` |Skew symmetric matrix associated with vector |<> +|=== + +[[operator:transpose,Operator transpose]] +Operator transpose:: ++ +[source,modelica] +---- +transpose(A) +---- ++ +Permutes the first two dimensions of array `A`. +It is an error if array `A` does not have at least 2 dimensions. + +[[function:outerProduct,Function outerProduct]] +Function outerProduct:: ++ +[source,modelica] +---- +outerProduct(x, y) +---- ++ +Returns the outer product of vectors `x` and `y`, that is: `matrix(x) * transpose(matrix(y))` + +[[function:symmetric,Function symetric]] +Function symetric:: ++ +[source,modelica] +---- +symmetric(A) +---- ++ +Returns a symmetric matrix which is identical to the square matrix `A` on and above the diagonal. ++ +That is, if `B := symmetric(A)`, then `B` is given by: ++ +[source,modelica] +---- +B[i, j] = + if i <= j then A[i, j] + else A[j, i] +---- + +[[function:cross,Function cross]] +Function cross:: ++ +[source,modelica] +---- +cross(x, y) +---- ++ +Returns the cross product of the 3-vectors `x` and `y`: ++ +[source,modelica] +---- +vector([ x[2] * y[3] - x[3] * y[2] ; + x[3] * y[1] - x[1] * y[3] ; + x[1] * y[2] - x[2] * y[1] ]) +---- + +[[function:skew,Function skew]] +Function skew:: ++ +[source,modelica] +---- +skew(x) +---- ++ +Returns the 3 × 3 skew symmetric matrix associated with a 3-vector, i.e., `cross(x, y) = skew(x) * y`. +Equivalently, `skew(x)` is given by: ++ +[source,modelica] +---- +[ 0, -x[3], x[2] ; + x[3], 0, -x[1] ; + -x[2], x[1], 0 ] +---- + +=== Vector, Matrix and Array Constructors + +The _array constructor_ function `array(A, B, C, ...)` constructs an array from its arguments according to the following rules: + +* Size matching: All arguments must have the same sizes, i.e., `size(A)` = `size(B)` = `+size(C)+` = ... + +* All arguments must be type compatible expressions (see <>) giving the type of the elements. + The data type of the result array is the maximally expanded type of the arguments. + Real and Integer subtypes can be mixed resulting in a Real result array where the Integer numbers have been transformed to Real numbers. + +* Each application of this constructor function adds a one-sized dimension to the left in the result compared to the dimensions of the argument arrays, i.e., `ndims(array(A, B, C))` = `ndims(A) + 1` = `ndims(B) + 1`, ... + +* `{A, B, C, ...}` is a shorthand notation for `array(A, B, C, ...)`. + +* There must be at least one argument. + +[NOTE] +The reason `array()` or `{}` is not defined is that at least one argument is needed to determine the type of the resulting array. + +[example] +==== +Example: + +[source,modelica] +---- +{1, 2, 3} // is a 3-vector of type Integer. +{{11, 12, 13}, {21, 22, 23}} // is a 2 x 3 matrix of type Integer +{{{1.0, 2.0, 3.0}}} // is a 1 x 1 x 3 array of type Real. + +Real[3] v = array(1, 2, 3.0); +type Angle = Real(unit="rad"); +parameter Angle alpha = 2.0; // type of alpha is Real. +// array(alpha, 2, 3.0) or {alpha, 2, 3.0} is a 3-vector of type Real. +Angle[3] a = {1.0, alpha, 4}; // type of a is Real[3]. +---- +==== + +==== Constructor with Iterators + +An expression: +[source,modelica] +---- +"{" expression for iterators "}" +---- +or +[source,modelica] +---- +array "(" expression for iterators ")" +---- +is an _array constructor with iterators_. +The expressions inside the iterators of an array constructor shall be vector expressions. +If `expression` contains event-generating expressions, the expressions inside the iterators shall be evaluable. +They are evaluated once for each array constructor, and are evaluated in the scope immediately enclosing the array constructor. + +For an iterator: + +[source,modelica] +---- +IDENT in array_expression +---- + +the loop-variable, `IDENT`, is in scope inside expression in the array construction. +The loop-variable may hide other variables, as in `for`-loops. +The loop-variable has the same type as the type of the elements of `array_expression`; and can be simple type as well as a record type. +The loop-variable will have the same type for the entire loop -- i.e., for an `array_expression {1, 3.2}` the iterator will have the type of the type-compatible expression (`Real`) for all iterations. +For deduction of ranges, see <>; and for using types as range see <>. + +===== Constructor with One Iterator + +If only one iterator is used, the result is a vector constructed by evaluating expression for each value of the loop-variable and forming an array of the result. + +[example] +==== +Example: + +[source,modelica] +---- +array(i for i in 1:10) +// Gives the vector 1:10 = {1, 2, 3, ..., 10} + +{r for r in 1.0 : 1.5 : 5.5} +// Gives the vector 1.0:1.5:5.5 = {1.0, 2.5, 4.0, 5.5} + +{i^2 for i in {1,3,7,6}} +// Gives the vector {1, 9, 49, 36} +---- +==== + +===== Constructor with Several Iterators + +The notation with several iterators is a shorthand notation for nested array constructors. +The notation can be expanded into the usual form by replacing each `,` by `} for` and prepending the array constructor with a `{`. + +[example] +==== +Example: + +[source,modelica] +---- +Real toeplitz[:,:] = {i-j for i in 1:n, j in 1:n}; +Real toeplitz2[:,:] = {{i-j for i in 1:n} for j in 1:n}; +---- +==== + +==== Concatenation + +The function `cat(k, A, B, C, ...)` concatenates arrays `A`, `B`, `C`, ... along dimension _k_ according to the following rules: + +* Arrays `A`, `B`, `C`, ... must have the same number of dimensions, i.e., `ndims(A) = ndims(B) = ...` + +* Arrays `A`, `B`, `C`, ... must be type compatible expressions (see <>) giving the type of the elements of the result. + The maximally expanded types should be equivalent. + `Real` and `Integer` subtypes can be mixed resulting in a `Real` result array where the `Integer` numbers have been transformed to `Real` numbers. + +* _k_ has to characterize an existing dimension, i.e., `1 ≤ k ≤ ndims(A) = ndims(B) = +ndims(C)+`; _k_ shall be a parameter expression of `Integer` type. + +* Size matching: Arrays `A`, `B`, `C`, ... must have identical array sizes with the exception of the size of dimension k, i.e., `size(A, j) = size(B, j)`, for `1 ≤ j ≤ ndims(A)+` and `j ≠ k`. + +[example] +==== +Example: + +[source,modelica] +---- +Real[2,3] r1 = cat(1, {{1.0, 2.0, 3}}, {{4, 5, 6}}); +Real[2,6] r2 = cat(2, r1, 2*r1); +---- +==== + +Formally, the concatenation `R = cat(k, A, B, C, ...)` is defined as follows. +Let `n` = `+ndims(A) = ndims(B) = ndims(C)+` ... +Then the size of R is given by + +---- +size(R,k) = size(A,k) + size(B,k) + size(C,k) + ... +size(R,j) = size(A,j) = size(B,j) = size(C,j) = ... for 1 ≤ j ≤ n and j ̸= k +---- + +and the array elements of R are given by + +[latexmath] +++++ +\begin{align*} +&R[i_{1}, \ldots, i_{k}, \ldots, i_{n}] = A[i_{1}, \ldots, i_{k}, \ldots, i_{n}]\\ +&\quad\text{for}\ \ 0 < i_{k} \leq size(A,k)\\ +&R[i_{1}, \ldots, i_{k}, \ldots, i_{n}] = B[i_{1}, \ldots, i_{k} - size(A,k), \ldots, i_{n}]\\ +&\quad\text{for}\ \ size(A,k) < i_{k} \leq size(A,k) + size(B,k)\\ +&R[i_{1}, \ldots, i_{k}, \ldots, i_{n}] = C[i_{1}, \ldots, i_{k} - size(A,k) - size(B,k), \ldots, i_{n}]\\ +&\quad\text{for}\ \ size(A,k) + size(B,k) < i_{k} \leq size(A,k) + size(B,k) + size(C,k)\\ +&\ldots +\end{align*} +++++ + +where `1 ≤ i~j~ ≤ size(R,j)` for `1 ≤ j ≤ n`. + +===== Concatenation along First and Second Dimensions + +For convenience, a special syntax is supported for the concatenation along the first and second dimensions: + +* Concatenation along first dimension: + `[A; B; C; ...] = cat(1, promote(A, n), promote(B, n), promote(C, n), ...)` where `n = max(2, ndims(A), ndims(B), +ndims(C)+, ...)`. + If necessary, 1-sized dimensions are added to the right of `A`, `B`, `C` before the operation is carried out, in order that the operands have the same number of dimensions which will be at least two. + +* Concatenation along second dimension: + `[A, B, C, ...] = cat(2, promote(A, n), promote(B, n), promote(C, n), ...)` where `n = max(2, ndims(A), ndims(B), +ndims(C)+, ...)`. + If necessary, 1-sized dimensions are added to the right of `A`, `B`, `C` before the operation is carried out, especially that each operand has at least two dimensions. + +* The two forms can be mixed. + `[..., ...]` has higher precedence than `[...; ...]`, e.g., `[a, b; c, d]` is parsed as `+[[a, b]; [c, d]]+`. + +* `[A] = promote(A, max(2, ndims(A)))`, i.e., `[A] = A`, if `A` has 2 or more dimensions, and it is a matrix with the elements of `A`, if `A` is a scalar or a vector. + +* There must be at least one argument (i.e., `[]` is not defined). + +[example] +==== +Example: +[source,modelica] +---- +Real s1, s2, v1[n1], v2[n2], M1[m1,n], +M2[m2,n], M3[n,m1], M4[n,m2], K1[m1,n,k], +K2[m2,n,k]; +[v1;v2] is a (n1+n2) x 1 matrix +[M1;M2] is a (m1+m2) x n matrix +[M3,M4] is a n x (m1+m2) matrix +[K1;K2] is a (m1+m2) x n x k array +[s1;s2] is a 2 x 1 matrix +[s1,s1] is a 1 x 2 matrix +[s1] is a 1 x 1 matrix +[v1] is a n1 x 1 matrix +Real[3] v1 = array(1, 2, 3); +Real[3] v2 = {4, 5, 6}; +Real[3,2] m1 = [v1, v2]; +Real[3,2] m2 = [v1, [4;5;6]]; // m1 = m2 +Real[2,3] m3 = [1, 2, 3; 4, 5, 6]; +Real[1,3] m4 = [1, 2, 3]; +Real[3,1] m5 = [1; 2; 3]; +---- +==== + +==== Vector Construction + +Vectors can be constructed with the general array constructor, e.g., +[source,modelica] +---- +Real[3] v = {1, 2, 3}; +---- +The range vector operator or colon operator of `simple-expression` can be used instead of or in combination with this general constructor to construct `Real`, `Integer`, `Boolean` or enumeration type vectors. +Semantics of the colon operator: + +* `j : k` is the `Integer` vector `{j, j+1, ..., k}`, if `j` and `k` are of type `Integer`. +* `j : k` is the `Real` vector `{j, j+1.0, ..., j+n}`, with `n = floor(k - j)`, if `j` and/or `k` are of type `Real`. +* `j : k` is a `Real`, `Integer`, `Boolean`, or enumeration type vector with zero elements, if `j > k`. +* `j : d : k` is the `Integer` vector `{j, j+d, ..., j + n d}`, with `n = div(k - j, d)`, if `j`, `d`, and `k` are of type `Integer`. +* `j : d : k` is the `Real` vector `{j, j+d, ..., j + n d}`, with `n = floor((k-j)/d)`, if `j`, `d`, or `k` are of type `Real`. + In order to avoid rounding issues for the length it is recommended to use `{j + d * i for i in 0 : n}` or `linspace(j, k, n + 1)` -- if the number of elements are known. +* `j : d : k` is a `Real` or `Integer` vector with zero elements, if `d > 0` and `j > k` or if `d < 0` and `j < k`. +* `false : true` is the `Boolean` vector `{false, true}`. +* `j : j` is `{j}` if j is `Real`, `Integer`, `Boolean`, or `enumeration` type. +* `E.ei : E.ej` is the enumeration type vector `{E.ei, ..., E.ej}` where `E.ej > E.ei`, and `ei` and `ej` belong to some enumeration type `E = enumeration(..., ei, ..., ej, ...)`. + +[example] +==== +Example: +[source,modelica] +---- +Real v1[5] = 2.7 : 6.8; +Real v2[5] = {2.7, 3.7, 4.7, 5.7, 6.7}; // = same as v1 +Boolean b1[2] = false:true; +Colors = enumeration (red,blue,green); +Colors ec[3] = Colors.red : Colors.green; +---- +==== + +=== Indexing + +The array indexing operator `name[...]` is used to access array elements for retrieval of their values or for updating these values. +An indexing operation is subject to upper and lower array dimension index bounds (see <>). +The indexing operator takes two or more operands, where the first operand is the array to be indexed and the rest of the operands are _index_ (or _subscript_) expressions: + +[source,subs=+quotes] +---- +arrayname[indexexpr~1~, indexexpr~2~, ...] +---- + +A colon (`:`) is used to denote all indices of one dimension. +A vector expression can be used to pick out selected rows, columns and elements of vectors, matrices, and arrays. +The number of dimensions of the expression is reduced by the number of scalar index arguments. +If the number of index arguments is smaller than the number of dimensions of the array, the trailing indices will use `:`. + +It is possible to index a general expression by enclosing it in parenthesis. +Note that while the subscripts are applied to an `output-expression-list` in the grammar, it is only semantically valid when the `output-expression-list` represents an expression. + +It is also possible to use the array access operator to assign to element/elements of an array in algorithm sections. +This is called an _indexed assignment statement_. +If the index is an array the assignments take place in the order given by the index array. +For assignments to arrays and elements of arrays, the entire right-hand side and the index on the left-hand side are evaluated before any element is assigned a new value. + +[NOTE] +An indexing operation is assumed to take constant time, i.e., largely independent of the size of the array. + +[example] +==== +Example: Array indexing expressions: + +[source,modelica] +---- +a[:, j] // Vector of the j'th column of a. +a[j] // Vector of the j'th row of a. Same as: a[j, :] +a[j : k] // Same as: {a[j], a[j+1], ..., a[k]} +a[:, j : k] // Same as: [a[:, j], a[:, j+1], ..., a[:, k]] +---- + +The range vector operator is just a special case of a vector expression: + +[source,modelica] +---- +v[2 : 2 : 8] // Same as: v[{2, 4, 6, 8}] +---- + +Array indexing in assignment statements: + +[source,modelica] +---- +v[{j, k}] := {2, 3}; // Same as: v[j] := 2; v[k] := 3; +v[{1, 1}] := {2, 3}; // Same as: v[1] := 3; +---- + +Array indexing of general expression: + +[source,modelica] +---- +(a*a)[:, j] // Vector of the j'th column of a*a +---- + +If `x` is a vector, `x[1]` is a scalar, but the slice `x[1:5]` is a vector (a vector-valued or colon index expression causes a vector to be returned). +==== + +.Examples of scalars vs. array slices created with the colon index. The examples make use of the array variables `x[n, m]`, `v[k]`, and `z[i, j, p]`. +[cols="a,^a,a",options=autowidth] +|=== +|Expression |# dims |Description + +|`x[1, 1]` |0 |Scalar +|`x[:, 1]` |1 |n-vector +|`x[1, :]` or `x[1]` |1 |m-vector +|`v[1:p]` |1 |p-vector +|`x[1:p, :]` |2 |p x m matrix +|`x[1:1, :]` |2 |1 x m "row" matrix +|`x[{1, 3, 5}, :]` |2 |3 x m matrix +|`x[:, v]` |2 |n x k matrix +|`z[:, 3, :]` |2 |i x p matrix +|`x[scalar([1]), :]` |1 |m-vector +|`x[vector([1]), :]` |2 |1 x m "row" matrix +|=== + +==== Boolean or Enumeration Indices + +Arrays can be indexed using values of enumeration types or the `Boolean` type, not only by `Integer`. +The type of the index should correspond to the type used for declaring the dimension of the array. + +[example] +==== +Example: + +[source,modelica] +---- +type ShirtSizes = enumeration(small, medium, large, xlarge); +Real[ShirtSizes] w; +Real[Boolean] b2; +algorithm + w[ShirtSizes.large] := 2.28; // Assign a value to an element of w + b2[true] := 10.0; + b2[ShirtSizes.medium] := 4; // Error, b2 was declared with Boolean dimension + w[1] := 3; // Error, w was declared with ShirtSizes dimension +---- +==== + +==== Indexing with end + +The expression `end` may only appear inside array subscripts, and if used in the i-th subscript of an array expression A it is equivalent to the upper bound of the i-th dimension of A. +If used inside nested array subscripts it refers to the most closely nested array. + +[NOTE] +If indices to `A` are a subtype of `Integer` it is equivalent to `size(A, i)`. + +[example] +==== +Example: + +[source,modelica] +---- +A[end - 1, end] // is A[size(A,1) - 1, size(A,2)] +A[v[end], end] // is A[v[size(v,1)], size(A,2)] // First end is referring to end of v. + +Real B[Boolean]; +B[end] // is B[true] +---- +==== + +=== Scalar, Vector, Matrix, and Array Operator Functions + +The mathematical operations defined on scalars, vectors, and matrices are the subject of linear algebra. + +The term numeric or numeric class is used below for a subtype of the `Real` or `Integer` type classes. +The standard type coercion defined in <> applies. + +==== Equality and Assignment + +Equality `a = b` and assignment `a := b` of scalars, vectors, matrices, and arrays is defined element-wise and require both objects to have the same number of dimensions and corresponding dimension sizes. +See <> regarding assignments to array variables with vector of subscripts. + +The operands need to be type equivalent. +This is legal for the simple types and all types satisfying the requirements for a record, and is in the latter case applied to each component-element of the records. + +.Equality and assignment of arrays and scalars. The scalar Operation applies for all j in 1,...,n and k in 1,...,m. +[cols="a,a,a,a",options=autowidth] +|=== +|Size of a |Size of b |Size of a = b |Operation + +|Scalar |Scalar |Scalar |`a = b` +|n-vector |n-vector |n-vector |`a[j] = b[j]` +|n × m matrix |n × m matrix |n × m matrix |`a[j, k] = b[j, k]` +|n × m × ... |n × m × ... |n × m × ... |`a[j, k, ...] = b[j, k, ...]` +|=== + +==== Addition, Subtraction, and String Concatenation + +Addition `a + b` and subtraction `a - b` of numeric scalars, vectors, matrices, and arrays is defined element-wise and require `size(a) = size(b)` and a numeric type for `a` and `b`. +Unary plus and minus are defined element-wise. +Addition `a + b` of string scalars, vectors, matrices, and arrays is defined as element-wise string concatenation of corresponding elements from `a` and `b`, and require `size(a) = size(b)`. + +.Array addition, subtraction, and string concatenation. In this table the symbolic operator ± represents either + or -. The scalar Operation applies for all j in 1,..., n and k in 1,...,m. +[cols="a,a,a,a",options=autowidth] +|=== +|Size of a |Size of b |Size of a ± b |Operation c := a ± + +|Scalar |Scalar |Scalar |`c := a ± b` +|n-vector |n-vector |n-vector |`c[j] := a[j] ± b[j]` +|n × m matrix |n × m matrix |n × m matrix |`c[j, k] := a[j, k] ± b[j, k]` +|n × m × ... |n × m × ... |n × m × ... |`c[j, k, ...] := a[j, k, ...] ± b[j, k, ...]` +|=== + +Element-wise addition `a .+ b` and subtraction `a .- b` of numeric scalars, vectors, matrices or arrays a and b requires a numeric type class for a and b and either `size(a) = size(b)` or scalar `a` or scalar `b`. +Element-wise addition `a .+ b` of string scalars, vectors, matrices, and arrays is defined as element-wise string concatenation of corresponding elements from a and b, and require either `size(a) = size(b)` or scalar `a` or scalar `b`. + +.Array element-wise addition, subtraction, and string concatenation. In this table the symbolic operator `±` represents either `+++` or `-`, and when preceded by a dot (.±), either `.+` or `.-`. The scalar Operation applies for all j in 1,...,n and k in 1,...,m. +[cols="a,a,a,a",options=autowidth] +|=== +|Size of a |Size of b |Size of a .± b |Operation c := a .± b + +|Scalar |Scalar |Scalar |`c := a ± b` +|Scalar |n × m × ... |n × m × ... |`c[j, k, ...] := a ± b[j, k, ...]` +|n × m × ... |Scalar |n × m × ... |`c[j, k, ...] := a[j, k, ...] ± b` +|n × m × ... |n × m × ... |n × m × ... |`c[j, k, ...] := a[j, k, ...] ± b[j, k, ...]` +|=== + +.Unary operators. In this table the symbolic operator `±` represents either unary `\+` or unary `-`. The element-wise (`.+`, `.-`) and normal (`+++`, `-`) operators give the same results. The scalar Operation applies for all j in 1,...,n and k in 1,...,m. +[cols="a,a,a",options=autowidth] +|=== +|Size of a |Size of ± a |Operation c := ± a + +|Scalar |Scalar |`c := ± a` +|n × m × ... |n × m × ... |`c[j, k, ...] := ± a[j, k, ...]` +|=== + +==== Element-wise Multiplication + +Scalar multiplication `s * a` or `a * s` with numeric scalar `s` and numeric scalar, vector, matrix or array `a` is defined element-wise: + +.Scalar and scalar to array multiplication of numeric elements. The scalar Operation applies for all j in 1,...,n and k in 1,...,m. +[cols="a,a,a,a",options=autowidth] +|=== +|Size of s |Size of a |Size of s * a and a * s |Operation c := s * a or c := a * s + +|Scalar |Scalar |Scalar |`c := s * a` +|Scalar |n-vector |n-vector |`c[j] := s * a[j]` +|Scalar |n × m matrix |n × m matrix |`c[j, k] := s * a[j, k]` +|Scalar |n × m × ... |n × m × ... |`c[j, k, ...] := s * a[j, k, ...]` +|=== + +Element-wise multiplication `a .* b` of numeric scalars, vectors, matrices or arrays `a` and `b` requires a numeric type class for `a` and `b` and either `size(a) = size(b)` or scalar `a` or scalar `b`. + +.Array element-wise multiplication. The scalar Operation applies for all j in 1,...,n and k in 1,...,m. +[[tab:product]] +[cols="a,a,a,a",options=autowidth] +|=== +|Size of a |Size of b |Size of a .* b |Operation c := a .* b + +|Scalar |Scalar |Scalar |`c := a * b` +|Scalar |n × m × ... |n × m × ... |`c[j, k, ...] := a * b[j, k, ...]` +|n × m × ... |Scalar |n × m × ... |`c[j, k, ...] := a[j, k, ...] * b` +|n × m × ... |n × m × ... |n × m × ... |`c[j, k, ...] := a[j, k, ...] * b[j, k, ...]` +|=== + +==== Multiplication of Matrices and Vectors + +Multiplication `a * b` of numeric vectors and matrices is defined only for the following combinations: + +.Matrix and vector multiplication of arrays with numeric elements. The scalar Operation applies for all i in 1,...,l and j in 1,...,n, and the summation over k goes from 1 to m. +[[tab:matrix-vector-multiplication]] +[cols="a,a,a,a",options=autowidth] +|=== +|Size of a |Size of b |Size of a * b |Operation c := a * b + +|m-vector |m-vector |Scalar |`c := sum_{k} a[k] * b[k]` +|m-vector |m × n matrix |n-vector |`c[j] := sum_{k} a[k] * b[k, j]` +|l × m matrix |m-vector |l-vector |`c[i] := sum_{k} a[i, k] * b[k]` +|l × m matrix |m × n matrix |l × n matrix |`c[i, j] := sum_{k} a[i, k] * b[k, j]` +|=== + +[example] +==== +Example: + +[source,modelica] +---- +Real A[3, 3], x[3], b[3], v[3]; +A * x = b; +x * A = b; // same as transpose([x])*A*b +[v] * transpose([v]) // outer product +v * A * v // scalar +transpose([v]) * A * v // vector with one element +---- +==== + +==== Division by Numeric Scalars + +Division `a / s` of numeric scalars, vectors, matrices, or arrays `a` and numeric scalars `s` is defined element-wise. +The result is always of `Real` type. +In order to get integer division with truncation, use the function `div`. + +.Division of scalars and arrays by numeric elements. The scalar Operation applies for all j in 1,...,n and k in 1,...,m. +[cols="a,a,a,a",options=autowidth] +|=== +|Size of a |Size of s |Size of a / s |Operation c := a / s + +|Scalar |Scalar |Scalar |`c := a / s` +|n-vector |Scalar |n-vector |`c[k] := a[k] / s` +|n × m matrix |Scalar |n × m matrix |`c[j, k] := a[j, k] / s` +|n × m × ... |Scalar |n × m × ... |`c[j, k, ...] := a[j, k, ...] / s` +|=== + +==== Element-wise Division + +Element-wise division `a ./ b` of numeric scalars, vectors, matrices or arrays `a` and `b` requires a numeric type class for `a` and `b` and either `size(a) = size(b)` or scalar `a` or scalar `b`. +The result is always of `Real` type. +In order to get integer division with truncation, use the function `div`. + +.Element-wise division of arrays. The scalar Operation applies for all j in 1,...,n and k in 1,...,m. +[cols="a,a,a,a",options=autowidth] +|=== +|Size of a |Size of b |Size of a ./ b |Operation c := a ./ b + +|Scalar |Scalar |Scalar |`c := a / b` +|Scalar |n × m × ... |n × m × ... |`c[j, k, ...] := a / b[j, k, ...]` +|n × m × ... |Scalar |n × m × ... |`c[j, k, ...] := a[j, k, ...] / b` +|n × m × ... |n × m × ... |n × m × ... |`c[j, k, ...] := a[j, k, ...] / b[j, k, ...]` +|=== + +[example] +==== +Example: Element-wise division by scalar (`./`) and division by scalar (`/`) are identical: `a ./ s = a / s`: + +[source,modelica] +---- +2./[1, 2; 3, 4] // error; same as 2.0 / [1, 2; 3, 4] +2 ./[1, 2; 3, 4] // fine; element-wise division +---- +This is a consequence of the parsing rules, since `2.` is a lexical unit. +Using a space after the literal solves the problem. +==== + +==== Element-wise Exponentiation + +Exponentiation `a ^ b` always returns a Real scalar value, and it is required that a and b are scalar Real or Integer expressions. +The result should correspond to mathematical exponentiation with the following special cases: + +* For any value of `a` (including `0.0`) and an Integer `b = 0`, the result is `1.0`. + +* If `a < 0` and `b` is an `Integer`, the result is defined as `±|a|^b^`, with sign depending on whether `b` is even (positive) or odd (negative). + +* A deprecated semantics is to treat `a < 0` and a `Real` `b` having a non-zero integer value as if `b` were an `Integer`. + +* For `a = 0` and `b > 0`, the result is `0.0`. + +* Other exceptional situations are illegal. + For example: `a = 0.0` and `b = 0.0` for a `Real` `b`, `a = 0.0` and `b < 0`, or `a < 0` and `b` does not have an integer value. + +[NOTE] +Except for defining the special case of `0.0^0^` it corresponds to `pow(double a, double b)` in the ANSI C library. +The result is always Real as negative exponents can give non-integer results also when both operands are `Integer`. +The special treatment of `Integer` exponents makes it possible to use x^n^ in a power series. + +Element-wise exponentiation `a .^ b` of numeric scalars, vectors, matrices, or arrays `a` and `b` requires a numeric type class for `a` and `b` and either `size(a) = size(b)` or scalar `a` or scalar `b`. + +.Element-wise exponentiation of arrays. The scalar Operation applies for all j in 1,...,n and k in 1,...,m. +[cols="a,a,a,a",options=autowidth] +|=== +|Size of `a` |Size of `b` |Size of `a .^ b ` |Operation `c := a .^ b` + +|Scalar |Scalar |Scalar |`c := a ^ b` +|Scalar |n × m × ... |n × m × ... |`c[j, k, ...] := a ^ b[j, k, ...]` +|n × m × ... |Scalar |n × m × ... |`c[j, k, ...] := a[j, k, ...] ^ b` +|n × m × ... |n × m × ... |n × m × ... |`c[j, k, ...] := a[j, k, ...] ^ b[j, k, ...]` +|=== + +[example] +==== +Example: + +[source,modelica] +---- +2.^[1, 2; 3, 4] // error; same as 2.0 ^ [1, 2; 3, 4] +2 .^[1, 2; 3, 4] // fine; element-wise exponentiation +---- + +This is a consequence of the parsing rules, i.e., since `2.` could be a lexical unit it seen as a lexical unit; using a space after literals solves the problem. +==== + +==== Scalar Exponentiation of Matrices + +Exponentiation `a ^ s` is defined if `a` is a square numeric matrix and `s` is a scalar as a subtype of Integer with `s >= 0`. +The exponentiation is done by repeated multiplication, e.g.: + +[source,modelica] +---- +a^3 = a * a * a; +a^0 = identity(size(a, 1)); +assert(size(a, 1) == size(a, 2), "Matrix must be square"); +a^1 = a; +---- + +[NOTE] +Non-Integer exponents are forbidden, because this would require computing the eigenvalues and eigenvectors of a and this is no longer an elementary operation. + +==== Slice Operation + +The following holds for slice operations: + +* If the component reference a is an array containing scalar components and m is a component of those components, the component reference a.m is interpreted as a slice operation. + It returns the array of components `{a[1].m, ...}`. + +* If m is also an array component, the slice operation is valid only if `size(a[1].m) = size(a[2].m) = ...` + +* The slicing operation can for component references be combined with indexing, e.g., `a.m[1]`. + It returns the array of components `{a[1].m[1], a[2].m[1], ...}`, and does not require that `size(a[1].m) = size(a[2].m)`. + The number of subscripts on m must not be greater than the number of array dimension for m (the number can be smaller, in which case the missing trailing indices are assumed to be `:`), and is only valid if `size(a[1].m[...]) = size(a[2].m[...]) = ...` + +* When the member access operator is applied to a record array, it is interpreted as constructing an array by selecting the member in each record. + If `m` in `(...).m` is also an array component of the record, the slice operation is valid only if the resulting array is homogenous. + +[example] +==== +Example: + +The size-restriction on the operand is only applicable if the indexing on the second operand uses vectors or colon as in the example: + +[source,modelica] +---- +constant Integer m=3; +Modelica.Blocks.Continuous.LowpassButterworth tf[m](n=2:(m+1)); +Real y[m]; +Real y2,y3; +equation + // Extract the x1 slice even though different x1's have different lengths + y = tf.x1[1] ; // Legal, = {tf[1].x1[1], tf[2].x1[1], ... tf[m].x1[1]}; + y2 = sum(tf.x1[:]); // Illegal to extract all elements since they have + // different lengths. Does not satisfy: + // size(tf[1].x1[:]) = size(tf[2].x1[:]) = ... = size(tf[m].x1[:]) + y3 = sum(tf.x1[1:2]); // Legal. + // Since x1 has at least 2 elements in all tf, and + // size(tf[1].x1[1:2]) = ... = size(tf[m].x1[1:2]) = {2} +---- +==== + +[example] +==== +Example: Member access slicing: + +[source,modelica] +---- + // Slice operation as part of component reference: + Complex c[2] = {Complex(1, 2), Complex(2, 3)} * Complex(1, 1); + Real x1[2]=c.im; + + // Same result, but slicing a general expression: + Real x2[2] = ({Complex(1, 2), Complex(2, 3)} * Complex(1, 1)).im; +---- +==== + +==== Relational Operators + +Relational operators `<`, `+<=+`, `>`, `>=`, `==`, `<>`, are only defined for scalar operands of simple types, not for arrays (see <>). + +==== Boolean Operators + +The operators `and` and `or` take expressions of Boolean type, which are either scalars or arrays of matching dimensions. +The operator `not` takes an expression of Boolean type, which is either scalar or an array. +The result is the element-wise logical operation. +For short-circuit evaluation of `and` and `or`, see <>. + +==== Vectorized Calls of Functions + +See <>. + +==== Standard Type Coercion + +In all contexts that require an expression which is a subtype of Real, an expression which is a subtype of Integer can also be used; +the Integer expression is automatically converted to Real. + +This also applies to arrays of Real, and for fields of record expressions. +There is no similar rule for sub-typing. + +[example] +==== +Example: + +[source,modelica] +---- +record RealR + Real x,y; +end RealR; +record IntegerR + Integer x,y; +end IntegerR; +parameter Integer a = 1; +Real y(start=a); // Ok, a is automatically coerced to Real +RealR r1 = IntegerR(a, a); // Ok, record is automatically coerced +RealR r2 = RealR(a, a); // Ok, a is automatically coerced to Real +---- +==== + +=== Empty Arrays + +Arrays may have dimension sizes of 0. +For example: + +[source,modelica] +---- +Real x[0]; // an empty vector +Real A[0, 3], B[5, 0], C[0, 0]; // empty matrices +---- + +Empty matrices can be constructed using the `fill` function. +For example: + +[source,modelica] +---- +Real A[:,:] = fill(0.0, 0, 1); // a Real 0 x 1 matrix +Boolean B[:, :, :] = fill(false, 0, 1, 0); // a Boolean 0 x 1 x 0 matrix +---- + +[example] +==== +Example: Whereas scalar indexing into an empty dimension of an array is an error, not all applications of indices to empty arrays are invalid: + +[source,modelica] +---- +Real[1, 0] a = fill(0.0, 1, 0); // a Real 1 x 0 matrix +Real[0] a1a = a[1]; // empty vector +Real[0] a1b = a[1, :]; // same as above +Real[0] a1c = a[1, 1 : end]; // same as above, as 1 : end is empty +---- +==== + +Size-requirements of operations, such as `+`, `-`, must also be fulfilled if a dimension is zero. +For example: + +[source,modelica] +---- +Real[3, 0] A, B; +Real[0, 0] C; +A + B // fine, result is an empty matrix +A + C // error, sizes do not agree +---- + +Multiplication of two empty matrices results in a zero matrix of corresponding numeric type if the result matrix has no zero dimension sizes, i.e., + +[source,modelica] +---- +Real[0, m] * Real[m, n] = Real[0, n] // empty matrix +Real[m, n] * Real[n, 0] = Real[m, 0] // empty matrix +Real[m, 0] * Real[0, n] = fill(0.0, m, n) // matrix of zeros +---- + +Note that `fill(0.0, m, n)` will be an empty matrix if m or n is zero. + +[example] +==== +Example: + +[source,modelica] +---- + Real u[p], x[n], y[q], A[n, n], B[n, p], C[q, n], D[q, p]; +equation + der(x) = A * x + B * u + y = C * x + D * u +---- + +Assume `n = 0`, `p > 0`, `q > 0`: Results in `y = D * u`. +==== \ No newline at end of file diff --git a/docs/11__statements.adoc b/docs/11__statements.adoc new file mode 100644 index 000000000..2aa2e15b8 --- /dev/null +++ b/docs/11__statements.adoc @@ -0,0 +1,560 @@ +== Statements and Algorithm Sections +:id: statements-and-algorithm-sections + +Whereas equations are very well suited for physical modeling, there are situations where computations are more conveniently expressed as algorithms, i.e., sequences of statements. +In this chapter we describe the algorithmic constructs that are available in Modelica. + +Statements are imperative constructs allowed in algorithm sections. + +=== Algorithm Sections + +An _algorithm section_ is a part of a class definition comprised of the keyword `algorithm` followed by a sequence of statements. +The formal syntax is as follows: + +[source,grammar] +---- +algorithm-section : + [ initial ] algorithm { statement ";" } +---- + +Like an equation, an algorithm section relates variables, i.e., constrains the values that these variables can take simultaneously. +In contrast to an equation section, an algorithm section distinguishes inputs from outputs: +An algorithm section specifies how to compute output variables as a function of given input variables. +A Modelica tool may actually invert an algorithm section, i.e., compute inputs from given outputs, e.g., by search (generate and test), or by deriving an inverse algorithm symbolically. + +Equation equality `=` or any other kind of equation (see <>) shall not be used in an algorithm section. + +==== Initial Algorithm Sections + +See <> for a description of both initial algorithm sections and initial equation sections. + +==== An Algorithm in a Model + +An algorithm section is conceptually a code fragment that remains together and the statements of an algorithm section are executed in the order of appearance. +Whenever an algorithm section is invoked, all variables appearing on the left hand side of the assignment operator `:=` are initialized (at least conceptually): + +* A continuous-time variable is initialized with the value of its `start` attribute. + +* A discrete-time variable `v` in a non-initial algorithm is initialized with `pre(v)`. + +* A clocked variable `v` in a discrete-time sub-partition, <>, is initialized with `previous(v)`. + +* If at least one element of an array appears on the left hand side of the assignment operator, then the complete array is initialized in this algorithm section. + +* In an initial algorithm, <>, any variable (including a parameter) is initialized with the value of its `start` attribute. + +[NOTE] +-- +Initialization is performed so that an algorithm section cannot introduce a "memory" (except in the case of discrete-time variables assigned in the algorithm), which could invalidate the assumptions of a numerical integration algorithm. +A Modelica tool may change the evaluation of an algorithm section, provided the result is identical to the case, as if the above conceptual processing is performed. + +For clocked variables it is important to skip this initialization when not needed, in order to avoid an excessive amount of clocked states, <>. + +An algorithm section is treated as an atomic vector-equation, which is sorted together with all other equations. +For the sorting process (BLT), every algorithm section with N different left-hand side variables is treated as an atomic N-dimensional vector-equation containing all variables appearing in the algorithm section. +This guarantees that all N equations end up in an algebraic loop and the statements of the algorithm section remain together. + +Example: + +[source,modelica] +---- +model Test // wrong Modelica model (has 4 equations for 2 unknowns) + Real x[2](start = {-11, -22}); +algorithm // conceptually: x = {1, -22} + x[1] := 1; +algorithm // conceptually: x = {-11, 2} + x[2] := 2; +end Test; +---- + +The conceptual part indicates that if the variable is assigned unconditionally in the algorithm before it is used the initialization can be omitted. +This is usually the case, except for algorithms with `when` statements, and especially for initial algorithms. +-- + +==== The Algorithm in a Function + +See <>. + +=== Statements + +Statements are imperative constructs allowed in algorithm sections. +A flattened statement is identical to the corresponding nonflattened statement. + +Names in statements are found as follows: + +* If the name occurs inside an expression: it is first found among the lexically enclosing reduction functions (see <>) in order starting from the inner-most, and if not found it proceeds as if it were outside an expression: + +* Names in a statement are first found among the lexically enclosing `for` statements in order starting from the inner-most, and if not found: + +* Names in a statement shall be found by looking up in the partially flattened enclosing class of the algorithm section. + +The syntax of statements is as follows: + +[source,grammar] +---- +statement : + ( component-reference ( ":=" expression | function-call-args ) + | "(" output-expression-list ")" ":=" + component-reference function-call-args + | break + | return + | if-statement + | for-statement + | while-statement + | when-statement + ) + description +---- + +==== Simple Assignment Statements + +The syntax of _simple assignment statement_ is as follows: + +[source,grammar] +---- +component-reference ":=" expression +---- + +The `expression` is evaluated. +The resulting value is stored into the variable denoted by `component-reference`. + +The `expression` must not have higher variability than the assigned component (see <>). + +Assignment to array variables with subscripts is described in <>. + +===== Assignments from Called Functions with Multiple Results + +There is a special form of assignment statement that is used only when the right-hand side contains a call to a function with multiple results. +The left-hand side contains a parenthesized, comma-separated list of variables receiving the results from the function call. +A function with n results needs m ≤ n receiving variables on the left-hand side, and the variables are assigned from left to right. + +[source,modelica] +---- +(out1, out2, out3) := function_name(in1, in2, in3, in4); +---- + +It is possible to omit receiving variables from this list: + +[source,modelica] +---- +(out1, , out3) := function_name(in1, in2, in3, in4); +---- + +[example] +==== +Example: The function `f` called below has three results and two inputs: + +[source,modelica] +---- +(a, b, c) := f(1.0, 2.0); +(x[1], x[2], x[1]) := f(3, 4); +---- + +In the second example above `x[1]` is assigned twice: first with the first output, and then with the third output. +For that case the following will give the same result: + +[source,modelica] +---- +(, x[2], x[1]) := f(3,4); +---- +==== + +The syntax of an assignment statement with a call to a function with multiple results is as follows: + +[source,grammar] +---- +"(" output-expression-list ")" ":=" component-reference function-call-args +---- + +[NOTE] +Also see <> regarding calling functions with multiple results within equations. + +===== Assigned Variables - Restrictions + +Only components of the specialized classes `type`, `record`, `operator record`, and `connector` may appear as left-hand-side in algorithms. +This applies both to simple assignment statements, and the parenthesized, comma-separated list of variables for functions with multiple results. + +==== For-Statement + +The syntax of a `for` statement is as follows: + +[source,grammar] +---- +for for-indices loop + { statement ";" } +end for +---- + +A `for` statement may optionally use several iterators (`for-indices`), see <> for more information: + +[source,grammar] +---- +for-indices: + for-index { "," for-index } + +for-index: + IDENT [ in expression ] +---- + +The following is an example of a prefix of a `for` statement: + +[source,modelica] +---- +for IDENT in expression loop +---- + +The rules for `for` statements are the same as for `for` expressions in <> -- except that the `expression` of a `for` statement is not restricted to a parameter-expression. + +If the `for` statement contains event-generating expressions, any expression in `for-index` shall be evaluable. + +[NOTE] +In general, the same event-generating expression requires distinct crossing functions for different iterations of the `for` loop, and the restriction ensures that the number of crossing functions is known during translation time. + +[example] +==== +Example: + +[source,modelica] +---- +for i in 1 : 10 loop // i takes the values 1, 2, 3, ..., 10 +for r in 1.0 : 1.5 : 5.5 loop // r takes the values 1.0, 2.5, 4.0, 5.5 +for i in {1, 3, 6, 7} loop // i takes the values 1, 3, 6, 7 +for i in TwoEnums loop // i takes the values TwoEnums.one, TwoEnums.two + // for TwoEnums = enumeration(one, two) +---- + +The loop-variable may hide other variables as in the following example. +Using another name for the loop-variable is, however, strongly recommended. + +[source,modelica] +---- + constant Integer j = 4; + Real x[j]; +equation + for j in 1:j loop // The loop-variable j takes the values 1, 2, 3, 4 + x[j] = j; // Uses the loop-variable j + end for; +---- +==== + +===== Implicit Iteration Ranges + +An iterator `IDENT in range-expr` without the `in range-expr` requires that the `IDENT` appears as the subscript of one or several subscripted expressions, where the expressions are not part of an array in a component of an expandable connector. +The dimension size of the array expression in the indexed position is used to deduce the `range-expr` as `1:size(array-expression,indexpos)` if the indices are a subtype of `Integer`, or as `E.e1:E.en` if the indices are of an enumeration type `E = enumeration(e1, ..., en)`, or as `false:true` if the indices are of type `Boolean`. +If it is used to subscript several expressions, their ranges must be identical. +There may not be assignments to the entire arrays that are subscripted with `IDENT` inside the loop, but there may be assignments to individual elements or ranges of elements. + +[NOTE] +The size of an array -- the iteration range -- can be evaluated on entry to the `for` loop, since the array size cannot change during the execution of the `for`-loop. + +The `IDENT` may also, inside a reduction expression, array constructor expression, `for` statement, or `for` equation, occur freely outside of subscript positions, but only as a reference to the variable `IDENT`, and not for deducing ranges. +The `IDENT` may also be used as a subscript for an array in a component of an expandable connector but it is only seen as a reference to the variable `IDENT` and cannot be used for deducing ranges. + +[example] +==== +Example: Implicit iterator ranges for an `Integer` subscript: + +[source,modelica] +---- + Real x[4]; + Real xsquared[:] = {x[i] * x[i] for i}; + // Same as: {x[i] * x[i] for i in 1 : size(x, 1)} + Real xsquared2[size(x, 1)]; + Real xsquared3[size(x, 1)]; +equation + for i loop // Same as: for i in 1 : size(x, 1) loop ... + xsquared2[i] = x[i]^2; + end for; +algorithm + for i loop // Same as: for i in 1 : size(x, 1) loop ... + xsquared3[i] := x[i]^2; + end for; +---- +==== + +[example] +==== +Example: An array dimension's type of subscript does not matter for array compatibility, only the size of the array dimension matters. +This is true also for array constructor expressions with implicit iterator ranges: + +[source,modelica] +---- +type FourEnums = enumeration(one, two, three, four); +Real x[4]; +Real xe[FourEnums] = x; +Real xsquared3[FourEnums] = {xe[i] * xe[i] for i in FourEnums}; +Real xsquared4[FourEnums] = {xe[i] * xe[i] for i}; +Real xsquared5[FourEnums] = {x[i] * x[i] for i}; +---- +==== + +===== Types as Iteration Ranges + +The iteration range can be specified as `Boolean` or as an enumeration type. +This means iteration over the type from min to max, i.e., for `Boolean` it is the same as `false:true` and for an enumeration `E` it is the same as `E.min:E.max`. +This can be used for `for`-loops and reduction expressions. + +[example] +==== +Example: + +[source,modelica] +---- + type FourEnums = enumeration(one, two, three, four); + Real xe[FourEnums]; + Real xsquared1[FourEnums]; + Real xsquared2[FourEnums] = {xe[i] * xe[i] for i in FourEnums}; +equation + for i in FourEnums loop + xsquared1[i] = xe[i]^2; + end for; +---- +==== + +===== Nested For-Loops and Reduction Expressions with Multiple Iterators + +The notation with several iterators is a shorthand notation for nested `for`-statements or `for`-equations (or reduction expressions). +For `for`-statements or `for`-equations it can be expanded into the usual form by replacing each `,` by `loop for` and adding extra `end for`. +For reduction expressions it can be expanded into the usual form by replacing each `,` by `) for` and prepending the reduction expression with `functionName(`. + +[example] +==== +Example: + +[source,modelica] +---- + Real x[4,3]; +algorithm + for j, i in 1:2 loop + // The loop variable j takes the values 1, 2, 3, 4 (due to use) + // The loop variable i takes the values 1, 2 (given range) + x[j,i] := j+i; + end for; +---- +==== + +==== While-Statement + +The `while`-statement has the following syntax: + +[source,grammar] +---- +while expression loop + { statement ";" } +end while +---- + +The `expression` of a `while`-statement shall be a scalar `Boolean` expression. + +The `while`-statement corresponds to while-statements in other programming languages, and is formally defined as follows: + +. The `expression` of the `while`-statement is evaluated. +. If the `expression` of the `while` statement is false, the execution continues after the `while`-statement. +. If the `expression` of the `while`-statement is true, the entire body of the `while` statement is executed (except if a `break`-statement, see <>, or a `return`-statement, see <>, is executed), and then execution proceeds at step 1. + +Event-generating expressions are neither allowed in the `expression` nor in the loop body statements. +A deprecated feature is that all expressions in a `while`-statement are implicitly inside `noEvent`. + +==== Break-Statement + +The `break`-statement breaks the execution of the innermost `while` or `for` loop enclosing the `break`-statement and continues execution after the `while` or `for` loop. +It can only be used in a `while` or `for` loop in an algorithm section. +It has the following syntax: + +[source,modelica] +---- +break; +---- + +[example] +==== +Example: (Note that this could alternatively use `return`.) + +[source,modelica] +---- +function findValue "Returns position of val or 0 if not found" + input Integer x[:]; + input Integer val; + output Integer index; +algorithm + index := size(x, 1); + while index >= 1 loop + if x[index] == val then + break; + else + index := index - 1; + end if; + end while; +end findValue; +---- +==== + +==== Return-Statements + +Can only be used inside functions, see <>. + +==== If-Statement + +The `if`-statements have the following syntax: + +[source,grammar] +---- +if expression then + { statement ";" } +{ elseif expression then + { statement ";" } +} +[ else + { statement ";" } +] +end if +---- + +The `expression` of an `if`- or `elseif`-clause must be scalar `Boolean` expression. +One `if`-clause, and zero or more `elseif`-clauses, and an optional `else`-clause together form a list of branches. +One or zero of the bodies of these `if`-, `elseif`-, and `else`-clauses is selected, by evaluating the conditions of the `if`- and `elseif`-clauses sequentially until a condition that evaluates to true is found. +If none of the conditions evaluate to true the body of the `else`-clause is selected (if an `else`-clause exists, otherwise no body is selected). +In an algorithm section, the selected body is then executed. +The bodies that are not selected have no effect on that model evaluation. + +==== When-Statements + +A `when`-statement has the following syntax: + +[source,grammar] +---- +when expression then + { statement ";" } +{ elsewhen expression then + { statement ";" } +} +end when +---- + +The `expression` of a `when`-statement shall be a discrete-time `Boolean` scalar or vector expression. +The statements within a `when`-statement are activated only at the instant when the scalar or any one of the elements of the vector expression becomes true. + +[example] +==== +Example: Algorithms are activated when `x` becomes > 2: + +[source,modelica] +---- +when x > 2 then + y1 := sin(x); + y3 := 2*x + y1+y2; +end when; +---- + +The statements inside the `when`-statement are activated on the positive edge of any of the expressions `x > 2`, `sample(0, 2)`, or `x < 5`: + +[source,modelica] +---- +when {x > 2, sample(0, 2), x < 5} then + y1 := sin(x); + y3 := 2*x + y1+y2; +end when; +---- + +For `when`-statements in algorithm sections the order is significant and it is advisable to have only one assignment within the `when`-statement and instead use several algorithm sections having `when`-statements with identical conditions, e.g.: + +[source,modelica] +---- +algorithm + when x > 2 then + y1 := sin(x); + end when; +equation + y2 = sin(y1); +algorithm + when x > 2 then + y3 := 2 * x + y1 + y2; + end when; +---- + +Merging the `when`-statements can lead to less efficient code and different models with different behavior depending on the order of the assignment to `y1` and `y3` in the algorithm. +==== + +===== Where a When-Statement May Occur + +* A `when`-statement shall not be used within a function. + +* A `when`-statement shall not occur inside an initial algorithm. + +* A `when`-statement cannot be nested inside another `when`-statement. + +* `when`-statements shall not occur inside `while` loops, `for` loops, or `if`-statements in algorithms. + +[example] +==== +Example: The following nested `when`-statement is invalid: + +[source,modelica] +---- +when x > 2 then + when y1 > 3 then + y2 := sin(x); + end when; +end when; +---- +==== + +===== Statements within When-Statements + +[NOTE] +-- +In contrast to `when`-equations, <> , there are no additional restrictions within `when`-statements: + +* In algorithms, all assignment statements are already restricted to left-hand-side variables. + +* If at least one element of an array appears on the left-hand-side of the assignment operator inside a `when`-statement, it is as if the entire array appears in the left-hand-side according to <>. + Thus, there is no need to restrict the indices to parameter-expressions. + +* The `for` loops and `if`-statements are not problematic inside `when`-statements in algorithms, since all left-hand-side variables inside `when`-statements are assigned to their pre-values before the start of the algorithm, according to <>. +-- + +===== Defining When-Statements by If-Statements + +A `when`-statement: +[source,modelica] +---- +algorithm + when {x > 1, ..., y > p} then + ... + elsewhen x > y.start then + ... + end when; +---- +is similar to the following special `if`-statement, where `Boolean b1[N];` and `Boolean b2;` are necessary because `edge` can only be applied to variables + +[source,modelica] +---- + Boolean b1[N](start = {x.start > 1, ..., y.start > p}); + Boolean b2(start = x.start > y.start); +algorithm + b1 := {x > 1, ..., y > p}; + b2 := x > y.start; + if edge(b1[1]) or edge(b1[2]) or ... or edge(b1[N]) then + ... + elseif edge(b2) then + ... + end if; +---- + +with `edge(A) = A and not pre(A)` and the additional guarantee, that the statements within this special `if`-statement are only evaluated at event instants. +The difference compared to the when-statements is that, e.g., `pre` may only be used on continuous-time real variables inside the body of a `when`-clause and not inside these if-statements. + +==== Special Statements + +These special statements have the same form and semantics as the corresponding equations, apart from the general difference in semantics between equations and statements. + +===== Assert-Statement + +See <>. +A failed `assert`-statement stops the execution of the current algorithm. + +===== Terminate-Statement + +See <>. +The `terminate`-statement shall not be used in functions. +In an algorithm outside a function it does not stop the execution of the current algorithm. \ No newline at end of file diff --git a/docs/12__functions.adoc b/docs/12__functions.adoc new file mode 100644 index 000000000..40816617e --- /dev/null +++ b/docs/12__functions.adoc @@ -0,0 +1,2860 @@ +== Functions +:id: functions + +This chapter describes the Modelica function construct. + +=== Function Declaration + +A Modelica _function_ is a specialized class (see <>) 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 (see <>). +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`. + +[example] +==== +The structure of a typical function declaration is sketched by the following schematic function example: + +[source,modelica] +---- +function functionname + input TypeI1 in1; + input TypeI2 in2; + input TypeI3 in3 = defaultExpr_1 "Comment" annotation(...); + ... + output TypeO1 out1; + output TypeO2 out2 = defaultExpr_2; + ... +protected + // local variables + ... +algorithm + // statements + ... +end functionname; +---- +==== + +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. + +[NOTE] +Explicit default values are shown for the third input parameter and the second output parameter in the example above. + +[NOTE] +-- +All internal parts of a function are optional; i.e., the following is also a legal function: + +[source,modelica] +---- +function functionname +end functionname; +---- +-- + +==== Ordering of Formal Parameters + +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. + +[NOTE] +Mixing declarations in this way is not recommended, however, since it makes the code hard to read. + +[example] +===== +Example: + +[source,modelica] +---- +function functionname + output TypeO1 out1; // Intermixed declarations of inputs and outputs + input TypeI1 in1; // not recommended since code becomes hard to read + input TypeI2 in2; + ... + output TypeO2 out2; + input TypeI3 in3; + ... +end functionname; +---- +===== + +==== Function Return-Statements + +The `return` statement terminates the current function call, see <>. +It can only be used in an algorithm section of a function. +It has the following form: + +[source,modelica] +---- +return; +---- + +[example] +===== +Example: (Note that this could alternatively use `break:`) + +[source,modelica] +---- +function findValue "Returns position of val or 0 if not found" + input Integer x[:]; + input Integer val; + output Integer index; +algorithm + for i in 1:size(x,1) loop + if x[i] == val then + index := i; + return; + end if; + end for; + index := 0; + return; +end findValue; +---- +===== + +==== Inheritance of Functions + +It is allowed for a function to inherit and/or modify another function following the usual rules for inheritance of classes (see <>). + +[NOTE] +For example, it is possible to modify and extend a `function` class to add default values for input variables. + +A special case is defining a `function` as a short-class definition with modifiers for inputs inside a model. +These default values, unless overridden in the function call, will then be considered for variability similarly as if they were given in the function call (see <>). + +[example] +==== +Example: Demonstrating the variability implications. Note that functions cannot directly use nonconstants in enclosing scopes, so we cannot write `input Real x1 = x;` directly in `foo`. + +[source,modelica] +---- +model M + function foo + input Real x1; + input Real x2 = 2; + output Real y; + algorithm + y := x1 + x2; + end foo; + Real x = time; + function f1 = foo(x1 = x); + constant Real z1 = f1(x1 = 2); // Legal, since 'x1' has a new value. + constant Real z2 = f1(x2 = 1); // Illegal, since 'x' is seen as an argument. +end M; +---- +==== + +=== Function as a Specialized Class + +The function concept in Modelica is a specialized class (see <>). + +[NOTE] +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 public component must have the prefix `input` or `output`. + +* 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. ++ +[NOTE] +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 <> and <>, and is not allowed to contain `when`-statements. + +* The dimension _sizes_ not declared with colon (`:`) of each array result or array local variable (i.e., a non-input component) 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 (see <>). + +* For initialization of local variables of a function see <>. + +* 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 (see <>). + +** The calls can use a mix of positional and named arguments (see <>). + +** Instances of functions have a special meaning (see <>). + +** The lookup of the `function` class to be called is extended (see <>). + +* 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 <>). + 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 colon (`:`) to be resized for non-input array variables (see <>). + +* A function may be defined in a short function definition to be a function partial derivative. + +=== Pure Modelica Functions + +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`. ++ +[NOTE] +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`, a diagnostic 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. + +[NOTE] +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. + +[NOTE] +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. + +[NOTE] +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(impureFunction(...))` -- which allows calling impure functions in any pure context. + The wrapping in `pure(...)` only by-passes the purity checking of the callee `impureFunction`; the argument expressions of the function call are not affected. + +* 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. ++ +[NOTE] +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. + +It is an error if an impure function call is part of a systems of equations (including linear systems), even if called in agreement with the restrictions above. +The reason is that solving systems of equations generally requires expressions to be evaluated an unknown number of times. +This includes the special handling of `when initial()` during initialization. + +There are two ways in which an impure function could be called in a system of equations, namely in the deprecated case of external functions assumed to be impure, and when using `pure(...)` to call an impure function from within a pure function. +The side-effect semantics of the function call are then undefined. +Specifically, the number of calls with external side-effects is unspecified. +However, for impure functions where the outputs only depend on the inputs the system of equations should be solved correctly. + +[NOTE] +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 <>, 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`. + +[example] +==== +Example: + +[source,modelica] +---- +function evaluateLinear // pure function + input Real a0; + input Real a1; + input Real x; + output Real y; +algorithm + y := a0 + a1*x; +end evaluateLinear; + +impure function receiveRealSignal // impure function + input HardwareDriverID id; + output Real y; +external "C" + y = receiveSignal(id); +end receiveRealSignal; +---- + +Examples of allowed optimizations of pure functions: + +[source,modelica] +---- +model M // Assume sin, cos, asin are pure functions with normal derivatives. + input Real x[2]; + input Real w; + Real y[2] = [cos(w), sin(w); -sin(w), cos(w)] * x; + Real z[2] = der(y); + Real a = 0 * asin(w); +end M; +---- + +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: + +[source,modelica] +---- + Real x = + if noEvent(abs(x)) < 1 then + asin(x) // Cannot move asin(x) out of if-branch. + else + 0; +algorithm + assertCheck(p, T); // Must call function +algorithm + if b then + y := 2 * someOtherFunction(x); + end if; + y := y + asin(x); + y := y + someOtherFunction(x); + // Cannot evaluate someOtherFunction(x) before asin(x) - unless b is true + // The reason is that asin(x) may fail and someOtherFunction may hang, + // and it might be possible to recover from this error. +---- +==== + +=== Function Call + +Function classes and record constructors (see <>) and enumeration type conversions (see <>) can be called as described in this section. + +==== Positional or Named Input Arguments + +A function call has optional positional arguments followed by zero, one or more named arguments, such as + +[source,modelica] +---- +f(3.5, 5.76, arg3=5, arg6=8.3); +---- + +The formal syntax of a function call (simplified by removing reduction expression, see <>): + +[source,grammar] +---- +primary : + component-reference function-call-args + +function-call-args : + "(" [ function-arguments ] ")" + +function-arguments : + function-argument [ "," function-arguments] + | named-arguments + +named-arguments: named-argument [ "," named-arguments ] + +named-argument: IDENT "=" function-argument + +function-argument : function-partial-application | expression +---- + +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. +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 (see <>) can be used to make the types agree. +(See also <> on applying scalar functions to arrays.) + +[example] +==== +Example: Assume a function `RealToString` is defined as follows to convert a `Real` number to a `String`: + +[source,modelica] +---- +function RealToString + input Real number; + input Real precision = 6 "number of significantdigits"; + input Real length = 0 "minimum length of field"; + output String string "number as string"; + ... +end RealToString; +---- + +Then the following applications are equivalent: + +[source,modelica] +---- +RealToString(2.0); +RealToString(2.0, 6, 0); +RealToString(2.0, 6); +RealToString(2.0, precision=6); +RealToString(2.0, length=0); +RealToString(2.0, 6, precision=6); // error: slot is used twice +---- +==== + +==== Functional Input Arguments + +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] +==== +Example: + +[source,modelica] +---- +function quadrature "Integrate function y = integrand(x) from x1 to x2" + input Real x1; + input Real x2; + input Integrand integrand; // Integrand is a partial function, see below + // With default: input Integrand integrand = Modelica.Math.sin; + output Real integral; +algorithm + integral := (x2 - x1) * (integrand(x1) + integrand(x2)) / 2; +end quadrature; + +partial function Integrand + input Real u; + output Real y; +end Integrand; +---- +==== + +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 (see <> 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 <> below). + +In all cases the provided function must be function-compatible (see <>) with the corresponding formal parameter of function type. + +[example] +==== +Example: A function as a positional input argument according to case 1 above: + +[source,modelica] +---- +function Parabola + extends Integrand; +algorithm + y := x * x; +end Parabola; +area = quadrature(0, 1, Parabola); +---- + +The `quadrature2` example below uses a function `integrand` that is a component as input argument according to case 3 above: +[source,modelica] +---- +function quadrature2 "Integrate function y = integrand(x) from x1 to x2" + input Real x1; + input Real x2; + input Integrand integrand; // Integrand is a partial function type + output Real integral; +algorithm + integral := + quadrature(x1, (x1 + x2) / 2, integrand) + + quadrature((x1 + x2) / 2, x2, integrand); +end quadrature2; +---- +==== + +===== Function Partial Application + +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 <>. +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.: + +[source,modelica] +---- +function func_name(..., formal_parameter_name = expr, ...) +---- + +[NOTE] +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 <>) with the same function where all bound arguments are removed. + +[NOTE] +Thus, for checking function type compatibility, bound formal parameters are ignored. + +[example] +==== +Example: Function partial application as argument, positional argument passing, according to case 2 above: +[source,modelica] +---- +model Test + parameter Integer N; + Real area; +algorithm + area := 0; + for i in 1:N loop + area := area + quadrature(0, 1, function Sine(A = 2, w = i * time)); + end for; +end Test; + +function Sine "y = Sine(x, A, w)" + extends Integrand; + input Real A; + input Real w; +algorithm + y := A * Modelica.Math.sin(w * x); +end Sine; +---- + +Call with function partial application as named input argument: + +[source,modelica] +---- +area := + area + quadrature(0, 1, integrand = function Sine(A = 2, w = i * time)); +---- +==== + +[example] +==== +Example: Function types are matching after removing the bound arguments `A` and `w` in a function partial application: + +[source,modelica] +---- +function Sine2 "y = Sine2(A, w, x)" + input Real A; + input Real w; + input Real x; // Note: x is now last in argument list. + output Real y; +algorithm + y := A * Modelica.Math.sin(w * x); +end Sine2; +area = quadrature(0, 1, integrand = function Sine2(A = 2, w = 3)); +---- + +The partially evaluated `Sine2` has only one argument: `x` -- and is thus type compatible with `Integrand`. +==== + +[example] +==== +Example: Function partial application of a function that is a component, according to case 4 above: + +[source,modelica] +---- +partial function SurfaceIntegrand + input Real x; + input Real y; + output Real z; +end SurfaceIntegrand; + +function quadratureOnce + input Real x; + input Real y1; + input Real y2; + input SurfaceIntegrand integrand; + output Real z; +algorithm + z := quadrature(y1, y2, function integrand(y = x)); + // This is according to case 4 and needs to bind the 2nd argument +end quadratureOnce; + +function surfaceQuadrature + input Real x1; + input Real x2; + input Real y1; + input Real y2; + input SurfaceIntegrand integrand; + output Real integral; +algorithm + integral := + quadrature(x1, x2, + function quadratureOnce(y1 = y1, y2 = y2, integrand = integrand)); + // Case 2 and 3 +end surfaceQuadrature; +---- +==== + +==== Output Formal Parameters + +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. + +[NOTE] +Optimizations to avoid computation of unused output results can be automatically deduced by an optimizing compiler. + +[example] +==== +Example: Function `eigen` to compute eigenvalues and optionally eigenvectors may be called in the following ways: + +[source,modelica] +---- +ev = eigen(A); // calculate eigenvalues +x = isStable(eigen(A)); // used in an expression +(ev, vr) = eigen(A) // calculate eigenvectors +(ev,vr,vl) = eigen(A) // and also left eigenvectors +(ev,,vl) = eigen(A) // no right eigenvectors +---- + +The function may be defined as: +[source,modelica] +---- +function eigen "calculate eigenvalues and optionally eigenvectors" + input Real A[:, size(A,1)]; + output Real eigenValues[size(A,1),2]; + output Real rightEigenVectors[size(A,1),size(A,1)]; + output Real leftEigenVectors [size(A,1),size(A,1)]; +algorithm + // The output variables are computed separately (and not, e.g., by one + // call of a Fortran function) in order that an optimizing compiler can + // remove unnecessary computations, if one or more output arguments are + // missing + // compute eigenvalues + // compute right eigenvectors using the computed eigenvalues + // compute left eigenvectors using the computed eigenvalues +end eigen; +---- +==== + +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] +==== +Example: The following are illegal: + +[source,modelica] +---- +(x+1, 3.0, z/y) = f(1.0, 2.0); // Not a list of component references. +(x, y, z) + (u, v, w) // Not LHS of suitable eqn/assignment. +---- +==== + +==== Initialization and Binding Equations + +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 the function do not have `start` attributes. +However, a binding equation (`= expression`) with an expression may be present for a component. + +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 <>. + +[NOTE] +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. + +[NOTE] +The properties of components in functions described in this section are also briefly described in <>. + +==== Flexible Array Sizes and Resizing of Arrays + +[NOTE] +Flexible setting of array dimension sizes of arrays in functions is also briefly described in <>. + +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] +==== +Example: + +[source,modelica] +---- +function joinThreeVectors + input Real v1[:],v2[:],v3[:]; + output Real vres[size(v1,1)+size(v2,1)+size(v3,1)]; +algorithm + vres := cat (1,v1,v2,v3); +end joinThreeVectors; +---- +==== + +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] +==== +Example: A function to collect the positive elements in a vector: + +[source,modelica] +---- +function collectPositive + input Real x[:]; + output Real xpos[:]; +algorithm + for i in 1 : size(x, 1) loop + if x[i] > 0 then + xpos := cat(1, xpos, x[i:i]); + end if; + end for; +end collectPositive; +---- +==== + +==== Automatic Vectorization + +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 (see <> and <>) 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. + +. 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 `e` with the same dimension sizes as the foreach arguments. +Each element `e[i, ..., 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. + +[example] +==== +Example: + +[source,modelica] +---- +sin({a, b, c}) = {sin(a), sin(b), sin(c)} // argument is a vector +sin([a, b, c]) = [sin(a), sin(b), sin(c)] // argument may be a matrix +atan2({a, b, c}, {d, e, f}) = {atan2(a, d), atan2(b, e), atan2(c, f)} +---- + +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`. + +[source,modelica] +---- +pval([1,2;3,4]) = [pval([1,2]); pval([3,4])] +sin([1,2;3,4]) = [sin({1,2}); sin({3,4})] + = [sin(1), sin(2); sin(3), sin(4)] +---- + +[source,modelica] +---- +function add + input Real e1, e2; + output Real sum1; +algorithm + sum1 := e1 + e2; +end add; +---- + +`add(1, [1,2,3])` adds one to each of the elements of the second argument giving the result `[2,3,4]`. +For built-in operators one can do this with `1 .+ [1,2,3]` but not with `1 + [1,2,3]`, because the rules for the built-in operators are more restrictive. +==== + +==== Empty Function Calls + +An _empty_ function call is a call that does not return any results. + +[NOTE] +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 <>). + +An empty call can occur either as a kind of "null equation" or "null statement". + +[example] +==== +Example: The empty calls to `eigen()` are examples of a "null equation" and a "null statement": + +[source,modelica] +---- +equation + Modelica.Math.Matrices.eigen(A); // Empty function call as an equation +algorithm + Modelica.Math.Matrices.eigen(A); // Empty function call as a statement +---- +==== + +=== Built-in Functions + +There are basically four groups of built-in functions in Modelica: + +* Intrinsic mathematical and conversion functions (see <>). + +* Derivative and special operators with function syntax (see <>). + +* Event-related operators with function syntax (see <>). + +* Built-in array functions (see <>). ++ +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. + +=== Record Constructor Functions + +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. + +[NOTE] +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. + +[example] +==== +This allows constructing an instance of a record, with an optional modification, at all places where a function call is allowed. + +Examples: + +[source,modelica] +---- + record Complex "Complex number" + Real re "real part"; + Real im "imaginary part"; + end Complex; + + function add + input Complex u, v; + output Complex w(re = u.re + v.re, im = u.im + v.re); + end add; + + Complex c1, c2; +equation + c2 = add(c1, Complex(sin(time), cos(time)); +---- + +In the following example, a convenient data sheet library of components is built up: +[source,modelica] +---- +package Motors + record MotorData "Data sheet of a motor" + parameter Real inertia; + parameter Real nominalTorque; + parameter Real maxTorque; + parameter Real maxSpeed; + end MotorData; + + model Motor "Motor model" // using the generic MotorData + MotorData data; + ... + equation + ... + end Motor; + + record MotorI123 = MotorData( // data of a specific motor + inertia = 0.001, + nominalTorque = 10, + maxTorque = 20, + maxSpeed = 3600) "Data sheet of motor I123"; + record MotorI145 = MotorData( // data of another specific motor + inertia = 0.0015, + nominalTorque = 15, + maxTorque = 22, + maxSpeed = 3600) "Data sheet of motor I145"; +end Motors + +model Robot + import Motors.*; + Motor motor1(data = MotorI123()); // just refer to data sheet + Motor motor2(data = MotorI123(inertia = 0.0012)); + // data can still be modified (if no final declaration in record) + Motor motor3(data = MotorI145()); + ... +end Robot; +---- + +Example showing most of the situations, which may occur for the implicit record constructor function creation. +With the following record definitions: + +[source,modelica] +---- +package Demo + record Record1 + parameter Real r0 = 0; + end Record1; + + record Record2 + import Modelica.Math.*; + extends Record1; + final constant Real c1 = 2.0; + constant Real c2; + parameter Integer n1 = 5; + parameter Integer n2; + parameter Real r1 "comment"; + parameter Real r2 = sin(c1); + final parameter Real r3 = cos(r2); + Real r4; + Real r5 = 5.0; + Real r6[n1]; + Real r7[n2]; + end Record2; +end Demo; +---- + +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): + +[source,modelica] +---- +package Demo + function Record1 + input Real r0 = 0; + output Record1 result(r0 = r0); + end Record1; + + function Record2 + input Real r0 = 0; + input Real c2; + input Integer n1 = 5; + input Integer n2; + input Real r1 "comment"; // the comment also copied from record + input Real r2 = Modelica.Math.sin(c1); + input Real r4; + input Real r5 = 5.0; + input Real r6[n1]; + input Real r7[n2]; + output Record2 result( + r0 = r0, c2 = c2, n1 = n1, n2 = n2, + r1 = r1, r2 = r2, r4 = r4, r5 = r5, r6 = r6, r7 = r7); + protected + final constant Real c1 = 2.0; // referenced from r2 + final parameter Real r3 = Modelica.Math.cos(r2); + end Record2; +end Demo; +---- + +and can be applied in the following way + +[source,modelica] +---- +Demo.Record2 r1 = + Demo.Record2(r0 = 1, c2 = 2, n1 = 2, n2 = 3, r1 = 1, r2 = 2, r4 = 5, r5 = 5, + r6 = {1, 2}, r7 = {1, 2, 3}); +Demo.Record2 r2 = + Demo.Record2(1, 2, 2, 3, 1, 2, 5, 5, {1, 2}, {1, 2, 3}); +parameter Demo.Record2 r3 = + Demo.Record2(c2 = 2, n2 = 1, r1 = 1, r4 = 4, r6 = 1 : 5, r7 = {1}); +---- + +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. +==== + +==== Casting to Record + +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 <>. + +[NOTE] +The problem if `R` would be a conditional component is that the corresponding binding would be illegal since it is not a `connect`-equation. + +[NOTE] +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] +==== +Example: + +[source,modelica] +---- +connector Flange + Real phi; + flow Real tau; +end Flange; + +model Model1 + Real m1; + Boolean b1; + Flange flange; +end Model1; + +model Model2 + Real r1; + Real r2; + Integer i2; + Pin p1, p2; + Model1 sub1; + protected + Integer i1; + ... +end Model2; + +record MyFlange + Real tau; +end MyFlange; + +record MyRecord1 + Boolean b1; + MyFlange flange; +end MyRecord1; + +record MyRecord2 + Real r1; + Integer i2; + MyRecord1 sub1; +end MyRecord2; + +model Model + Model2 s1; + Model2 s2[2]; + MyRecord2 rec1 = MyRecord2(s1); + MyRecord2 rec2[2] = MyRecord2(s2); + ... +end Model; +// Model is conceptually mapped to +model ModelExpanded + Model2 s1; + Model2 s2[2]; + MyRecord2 rec1 = MyRecord2(r1=s1.r1, i2=s1.i2, + sub1 = MyRecord1(b1=s1.sub1.b1, + flange = MyFlange(tau=s1.sub1.flange.tau)); + MyRecord2 rec2[2] = {MyRecord2(r1=s2[1].r1, i2=s2[1].i2, + sub1 = MyRecord1(b1=s2[1].sub1.b1, + flange = MyFlange(tau=s1[1].sub1.flange.tau)), + MyRecord2(r1=s2[2].r1, i2=s2[2].i2, + sub1 = MyRecord1(b1=s2[2].sub1.b1, + flange = MyFlange(tau=s2[2].sub1.flange.tau)}}; + ... +end ModelExpanded; +---- +==== + +=== Derivatives and Inverses of Functions + +The annotations listed below are related to differentiation and closed-form inverses of functions. +A function declaration can have `derivative` annotations specifying derivative functions or preferably, for a function written in Modelica, use the `smoothOrder` annotation to indicate that the tool can construct the derivative function automatically. +Partial derivatives are not provided via annotations, but using a certain type of short function definition described in <>. + +[cols="a,a,a",options=autowidth] +|=== +|Annotation |Description |Details + +|`smoothOrder` |Function smoothness guarantee |<> +|`derivative` |Provide function derivative |<> +|`inverse` |Provide closed-form inverses |<> +|=== + +[[annotation:smoothOrder,Annotation smoothOrder]] +Annotation smoothOrder:: ++ +[source,grammar] +---- +"smoothOrder" "=" UNSIGNED-NUMBER +"smoothOrder" + "(" + "normallyConstant" "=" IDENT + { "," "normallyConstant" "=" IDENT } + ")" + "=" UNSIGNED-NUMBER +---- ++ +This annotation has only an effect within a function declaration. ++ +`smoothOrder` defines the number of differentiations of the function, in order that all of the differentiated outputs are continuous provided all input arguments and their derivatives up to order `smoothOrder` are continuous. ++ +[NOTE] +-- +This means that the function is at least C^smoothOrder^. + +When a tool computes the derivative of a function, e.g., for index reduction or to compute an analytic Jacobian, each differentiation of a function reduces the `smoothOrder` by 1. +The `smoothOrder` information can then be used to infer continuity of the resulting differentiated function calls, provided the input arguments are continuous. +This is a conservative check, however, meaning that a tool may be able to establish continuity of a function call even though the `smoothOrder` has been reduced to less than 0, and/or some input arguments are not continuous. +-- ++ +The optional argument `normallyConstant` of `smoothOrder` defines that the function argument `IDENT` is usually constant. ++ +[NOTE] +-- +A tool might check whether the actual argument to `IDENT` is a parameter expression at the place where the function is called. +If this is the case, the derivative of the function might be constructed under the assumption that the corresponding argument is constant, to enhance efficiency. +Typically, a tool would generate at most two different derivative functions of a function: +One, under the assumption that all `normallyConstant` arguments are actually constant. +And one, under the assumption that all input arguments are time varying. +Based on the actual arguments of the function call either of the two derivative functions is used. + +This annotation is used by many functions of the `Modelica.Fluid` library, such as `Modelica.Fluid.Dissipation.PressureLoss.StraightPipe.dp_laminar_DP`, since geometric arguments to these functions are usually constant. +-- + +[[annotation:derivative,Annotation derivative]] +Annotation derivative:: ++ +[source,grammar] +---- +"derivative" [ derivative-constraints ] "=" name + +derivative-constraints : + "(" derivative-constraint { "," derivative-constraint } ")" + +derivative-constraint : + "order" = UNSIGNED-NUMBER + | "noDerivative" = IDENT + | "zeroDerivative" = IDENT +---- ++ +This annotation has only an effect within a function declaration. ++ +The `derivative` annotation can influence simulation time and accuracy, can be applied to both functions written in Modelica and to external functions, and may be used several times for the same function declaration. ++ +Each use of the `derivative` annotation points to another derivative-function that expresses a derivative of the declared function, and the annotation can state that it is only valid under certain restrictions on the input arguments. +These restrictions are defined using the optional attributes `order`, `noDerivative`, and `zeroDerivative`. +The `order` may be specified at most once for each `derivative` annotation, must be at least 1, and defaults to 1. +Specifying `order` is only considered a restriction if `order > 1`. ++ +For details abouts using the `derivative` annotation, see <>. + +[[annotation:inverse,Annotation inverse]] +Annotation inverse:: ++ +[source,grammar] +---- +"inverse" "(" function-inverse { "," function-inverse } ")" + +function-inverse : + IDENT "=" type-specifier function-call-args" +---- ++ +A function with one output formal parameter may have one or more `inverse` annotations to define inverses of this function. ++ +For details abouts using the `inverse` annotation, see <>. + +==== Using the Derivative Annotation + +The given derivative-function must be a valid derivative if the `derivative` annotation restrictions are satisfied, and can thus be used to compute the derivative in those cases. +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). +When a function supplies 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. + +[NOTE] +This means that the most restrictive derivatives should be written first. + +[example] +==== +Example: The following model illustrates the requirement that a provided derivative must be valid. +That `fder` is a valid derivative of `f` means that it can be used safely to compute `x2` by numeric integration: the function value, `x1`, will up to numerical precision be matched by the integral of the derivative, `x2`. + +[source,modelica] +---- +function f + input Real x; + output Real y; + annotation(derivative = fder); + external "C"; +end f; +model M + input Real u; + Real x1 "Directly from function"; + Real x2 "Integrated from derivative"; +equation + x1 = f(u); + der(x2) = der(x1); +initial equation + x2 = x1; +end M; +---- + +Note that tools are not required to use the provided derivative, and might solve the equations completely without numeric integration. +==== + +[example] +==== +Example: Use of `order` to specify a second order derivative: +[source,modelica] +---- +function foo0 annotation(derivative = foo1); +end foo0; + +function foo1 annotation(derivative(order=2) = foo2); +end foo1; + +function foo2 end foo2; +---- +==== + +The inputs and outputs of 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 __n__th derivative (_n_ ≥ 1), i.e., this function call has been derived from an (_n_-1)th derivative by differentiation inside the tool, an `annotation(derivative(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 __n__th `order` derivatives. + +* The output arguments are similar to the output argument for the __n__th 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. + +[NOTE] +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] +==== +Example: Given the declarations + +[source,modelica] +---- +function foo0 + ... + input Real x; + input Boolean linear; + input ...; + output Real y; + ... + annotation(derivative = foo1); +end foo0; + +function foo1 + ... + input Real x; + input Boolean linear; + input ...; + input Real der_x; + ... + output Real der_y; + ... + annotation(derivative(order=2) = foo2); +end foo1; + +function foo2 + ... + input Real x; + input Boolean linear; + input ...; + input Real der_x; + ...; + input Real der_2_x; + ... + output Real der_2_y; + ... +---- + +the equation + +[latexmath] +++++ +(\ldots,\, y(t),\, \ldots) = \text{foo0}(\ldots,\, x(t),\, b,\ldots) +++++ + +implies that: + +[latexmath] +++++ +(\ldots,\, \frac{dy(t)}{dt},\, \ldots) = \text{foo1}(\ldots,\, x(t),\, b,\, \ldots,\, \ldots,\, \frac{dx(t)}{dt},\, \ldots) +++++ + +[latexmath] +++++ +(\ldots,\, \frac{d^2y(t)}{dt^2},\, \ldots) = \text{foo2}(\ldots,\, x(t),\, b,\, \ldots,\, \frac{dx(t)}{dt},\, \ldots,\, \ldots,\, \frac{d^2x(t)}{dt^2},\, \ldots) +++++ +==== + +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] +==== +Example: Here is one example use case with records mixing `Real` and non-`Real` as inputs and outputs: + +[source,modelica] +---- +record ThermodynamicState "Thermodynamic state" + SpecificEnthalpy h "Specific enthalpy"; + AbsolutePressure p "Pressure"; + Integer phase(min = 1, max = 2, start = 1); +end ThermodynamicState; + +record ThermoDynamicState_der "Derivative" + SpecificEnthalpyDerivative h "Specific enthalphy derivative"; + PressureDerivative p "Pressure derivative"; + // Integer input is skipped +end ThermodynamicState_der; + +function density + input ThermodynamicState state "Thermodynamic state"; + output Density d "Density"; +algorithm + ... + annotation(derivative = density_der); +end density; + +function density_der + input ThermodynamicState state "Thermodynamic state"; + input ThermodynamicState_der state_der; + output DensityDerivative d "Density derivative"; +algorithm + ... +end density_der; + +function setState_ph + input Pressure p; + input SpecificEnthalpy h; + input Integer phase = 0; + output ThermodynamicState state; +algorithm + ... + annotation(derivative = setState_ph_der); +end setState_ph; + +function setState_ph_der + input Pressure p; + input SpecificEnthalpy h; + input Integer phase; + input PressureDerivative p_der; + input SpecificEnthalpyDerivative h_der; + output ThermodynamicState_der state_der; +algorithm + ... +end setState_ph_der; + +ThermodynamicState state1 = setState_ph(p=..., h=..., phase=...); +Density rho1 = density(state1); +DensityDerivative d_rho1 = der(rho1); +Density rho2 = density(setState_ph(p=..., h=..., phase=...)); +DensityDerivative d_rho2 = der(rho2); +---- +==== + +* `"zeroDerivative" "=" inputVar~1~, { "," "zeroDerivative" "=" inputVar~2~ }` + +The derivative function is only valid if `inputVar~1~` (and `inputVar~2~` 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 `inputVar~1~` (and `inputVar~2~` 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`. + +[example] +==== +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 = `fGeneralDer` is optional and can be used when the derivative of the matrix or offset is non-zero). +Note that the derivative annotation of `fDer` must specify `zeroDerivative` for both `y` and `offset` as below, but the derivative annotation of `fGeneralDer` shall not have `zeroDerivative` for either of them (it may specify `zeroDerivative` for `x_der`, `y_der`, or `offset_der`). + +[source,modelica] +---- +function f "Simple table lookup" + input Real x; + input Real y[:, 2]; + input Real offset "Shortened to o below"; + output Real z; +algorithm + ... + annotation(derivative(zeroDerivative=y, zeroDerivative=offset) = fDer, + derivative = fGeneralDer); +end f; + +function fDer "Derivative of simple table lookup" + input Real x; + input Real y[:, 2]; + input Real offset; + input Real x_der; + output Real z_der; +algorithm + ... + annotation( + derivative(zeroDerivative=y, zeroDerivative=offset, order=2) = fDer2); +end fDer; + +function fDer2 "Second derivative of simple table lookup" + input Real x; + input Real y[:, 2]; + input Real offset; + input Real x_der; + input Real x_der2; + output Real z_der2; +algorithm + ... +end fDer2; + +function fGeneralDer "Derivative of table lookup taking +into account varying tables" + input Real x; + input Real y[:, 2]; + input Real offset; + input Real x_der; + input Real y_der[:, 2]; + input Real offset_der; + output Real z_der; +algorithm + ... + //annotation(derivative(order=2) = fGeneralDer2); +end fGeneralDer; +---- + +In the example above `zeroDerivative=y` and `zeroDerivative=offset` imply that + +[latexmath] +++++ +\begin{align} +\frac{d}{dt}f(x(t),\, y(t),\, o(t)) +&= \frac{\partial f}{\partial x} \frac{dx}{dt} + \frac{\partial f}{\partial y} \frac{dy}{dt} + \frac{\partial f}{\partial o} \frac{do}{dt}\\ +&= \frac{\partial f}{\partial x} \frac{dx}{dt} + \frac{\partial f}{\partial y} \cdot 0 + \frac{\partial f}{\partial o} \cdot 0\\ +&= \frac{\partial f}{\partial x} \frac{dx}{dt}\\ +&= fDer \cdot \frac{dx}{dt} +\end{align} +++++ +==== + +* `"noDerivative" "=" inputVar~1~` + +The derivative of `inputVar~1~` 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). + +[example] +==== +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: + +[source,modelica] +---- +function fg + input Real x; + output Real z; +algorithm + z := f(x, g(x)); +end fg; + +function f + input Real x; + input Real y; + output Real z; +algorithm + ... + annotation(derivative(noDerivative=y) = h); +end f; + +function h + input Real x; + input Real y; + input Real x_der; + output Real z_der; +algorithm + ... +end h; +---- + +This is useful if `g` represents the major computational effort of `fg`. + +Therefore `h` indirectly includes the derivative with respect to `y` as follows: + +[latexmath] +++++ +\begin{align} +\frac{d}{dt}fg(x(t)) +&= \frac{d}{dt}f(x(t),\, g(x(t))) \\ +&= \frac{\partial f}{\partial x} \frac{dx}{dt} + \frac{\partial f}{\partial y} \frac{\partial g}{\partial x} \frac{dx}{dt} \\ +&= \left(\frac{\partial f}{\partial x} + \frac{\partial f}{\partial y} \frac{\partial g}{\partial x} \right) \frac{dx}{dt} \\ +&= h(x(t),\, y(t))) \frac{dx}{dt} +\end{align} +++++ +==== + +==== Partial Derivatives of Functions + +A class defined as: +[source,grammar] +---- +IDENT "=" der "(" name "," IDENT { "," IDENT } ")" comment +---- +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). +Each `IDENT` must be a scalar `Real` input 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] +==== +Example: The specific enthalpy can be computed from a Gibbs-function as follows: + +[source,modelica] +---- +function Gibbs + input Real p, T; + output Real g; +algorithm + ... +end Gibbs; +function Gibbs_T = der(Gibbs, T); +function specificEnthalpy + input Real p, T; + output Real h; +algorithm + h := Gibbs(p, T) - T * Gibbs_T(p, T); +end specificEnthalpy; +---- +==== + +==== Using the Inverse Annotation + +If a function `f1` with one output formal parameter `y` can be restricted to an informally defined domain and codomain, such that the mapping of the input formal parameter `uk` to `y` is bijective for any fixed assignment to the other input formal parameters in the domain (see examples below), then it can be given an `inverse` annotation to provide an explicit inverse `f2` to this mapping, provided that the function is only applied on this domain: + +The `inverse` annotation takes the following form in a function declaration: +[source,modelica] +---- +function f1 + input A1 u1; + ... + input T1 uk; + ... + input Am um = am; + ... + input An un; + output T2 y; +algorithm + ... + annotation(inverse(uk = f2(..., y, ...)); +end f1; +---- + +In addition to `y`, the formal call to `f2` in the annotation shall also pass the other formal parameters (excluding `uk`) needed determine the inverse, see below. +The function `f2` must be an actual inverse, meaning that if `uk` is calculated as `uk = f2(..., y, ...)`, then the equality `y = f1(..., uk, ...)` is satisfied up to a certain precision, for all values of the input arguments of `f2(..., y, ...)` in the range and informal domain of `f1`. + +More than one inverse can be defined within the same `inverse` annotation, separated by commas: + +[source,modelica] +---- +annotation(inverse(uk = f2(..., y, ...), ui = f3(..., y, ...), ...)); +---- + +Function `f1` can have any number and types of formal parameters with and without default value. +The restriction is that the number of unknown variables (see <>) in the output formal parameter of both `f1` and `f2` must be the same and that `f2` should have a union of output and formal parameters that is the same or a subset of that union for `f1`, but the order of the formal parameters may be permuted. + +[example] +==== +Example: Inverse function with same union of formal parameters: + +[source,modelica] +---- +function h_pTX + input Real p "pressure"; + input Real T "temperature"; + input Real X[:] "mass fractions"; + output Real h "specific enthalpy"; +algorithm + ... + annotation(inverse(T = T_phX(p, h, X))); +end h_pTX; + +function T_phX + input Real p "pressure"; + input Real h "specific enthalpy"; + input Real X[:] "mass fractions"; + output Real T "temperature"; +algorithm + ... +end T_phX; +---- +==== + +The subset case is useful if `f1` computes the inverse of `f2` within a region, or up to a certain tolerance. +Then, `f1` may specify `f2` as inverse with fewer arguments, skipping the arguments for tolerance and/or the region. + +[example] +==== +Example: Inverse function with subset of formal parameters: + +[source,modelica] +---- +function inv_sine + input Real x; + input Real angleOrig; + output Real angle; + // Finds sine(angle) = x with angle closest to angleOrig. +algorithm + ... + annotation(inverse(x = sine(angle))); +end inv_sine; + +function sine + input Real angle; + output Real x; +algorithm + x := sin(angle); + // Note: No inverse. +end sine; +---- +==== + +Tools are not expected to verify the bijectiveness requirement, meaning that it is the user's responsibility to ensure that this requirement is fulfilled, and that tools can rely on the requirement as an assumption for symbolic manipulations when an inverse function is provided. + +There is no guarantee that a provided inverse will be used, and no rule for at which stage of symbolic processing it could be applied. +Inlining a function means that the possibility to apply provided inverses is lost. +Hence, the recommended inlining annotations -- if any -- for use together with the `inverse` annotation is either `Inline = false` or `LateInline = true`. + +[example] +==== +Example: If an inverse is provided, but the injectiveness part of the bijectiveness requirement is not fulfilled, this may introduce additional ambiguity to the solution of equations with multiple solutions. +Consider the following invalid use of the `inverse` annotation: + +[source,modelica] +---- +model NotInjective + function square + input Real x; + output Real y = x^2; + annotation(inverse(x = sqrt(y))); // Invalid!} + end square; + + parameter Real y0 = -1.0; + Real y(start = y0, fixed = true); + Real x(start = sign(y0) * sqrt(abs(y0))); // Good guess with same sign as y. +equation + der(y) = -y; + square(x) = abs(y); // Expecting continuous solution for x. +end NotInjective; +---- + +That the parameter `y0` may have any sign means the sign of `x` cannot be restricted in the informal domain of `square`, and hence that the injectiveness requirement cannot be fulfilled. +Without the `inverse` annotation, the nonlinear equation in `x` and `y` has an ambiguity, but it is generally expected that this is handled so that a continuous solution for `x` is obtained, meaning that it will keep the same sign as `y` throughout the simulation. +The additional ambiguity introduced by the `inverse` annotation is that if the provided inverse is used to solve the nonlinear equation instead of using a generic nonlinear equation solver based on local search, then the solution with positive sign is always obtained. +The lack of guarantees that a provided inverse will be used thus implies a worse ambiguity than what was present in the model before introducing the `inverse` annotation. +==== + +[example] +==== +Example: If an inverse is provided, but the surjectiveness part of the bijectiveness requirement is not fulfilled, this may introduce an invalid solution to equations that do not have a solution at all. +Consider the following invalid use of the `inverse` annotation: + +[source,modelica] +---- +model NotSurjective + function cube + input Real x; + output Real y = x ^ 3; + end cube; + + function cbrtPos "Cubic root of positive number" + input Real y; + output Real x; + algorithm + assert(y > 0, "Argument must be positive."); + x := exp(log(y) / 3); + annotation(inverse(y = cube(x))); // Invalid!} + end cbrtPos; + + Real x = 0.5 + sin(time); + Real y; +equation + cbrtPos(y) = x; // Calling cbrtPos requires y > 0. + annotation(experiment(StopTime = 10.0)); +end NotSurjective; +---- + +As the value of `x` varies over the interval `[-1, 1]`, but the range of `cbrtPos` is only `(0, ∞)`, the informal codomain of `cbrtPos` cannot be restricted such that the surjectiveness is fulfilled. +A valid solution to the equation in `x` and `y` must satisfy `y > 0`, and when no `inverse` annotation is given, a violation will be detected by a nonlinear solver applied directly to the equation. +When the (invalid) inverse provided by the `inverse` annotation is used, however, the equation gets transformed into + +[source,modelica] +---- + y = cube(x); +---- + +where the requirement `y > 0` can no longer be detected, resulting in a simulation result that does not fulfill the original model equations. +==== + +=== Function Inlining and Event Generation + +The annotations listed below affect inlining of functions and the related topic of event generation inside functions. +See <> regarding the notation used to describe the annotations. + +[cols="a,a,a",options=autowidth] +|=== +|Annotation |Description |Details + +|`Inline` |Inline function |<> +|`LateInline` |Inline after all symbolic transformations |<> +|`InlineAfterIndexReduction` |Inline after index reduction |<> +|`GenerateEvents` |Generate events for zero crossings in function |<> +|=== + +Inlining a function makes the statements of the function body accessible to symbolic operations, potentially leading to expression simplifications and more efficient solution of equations. +At the same time, another important consequence of inlining a function is that any annotations for derivatives or inverses are lost. +Hence, one needs to find the right balance between inlining too early (loss of provided derivatives and inverses) and too late (earlier stages of symbolic processing cannot benefit from symbolic simplifications). + +[[annotation:inline,Annotation Inline]] +Annotation Inline:: ++ +[source,modelica] +---- +/*literal*/ constant Boolean Inline; +---- ++ +Has only an effect within a function declaration. ++ +If `Inline = true`, the model developer proposes to inline the function. +This means, that the body of the function is included at all places where the function is called. ++ +If `Inline = false`, the model developer proposes to not inline the function. +The default for inlining is tool-specific. ++ +[NOTE] +`Inline = true` is for example used in `Modelica.Mechanics.MultiBody.Frames` and in functions of `Modelica.Media` to have no overhead for function calls such as resolving a vector in a different coordinate system and at the same time the function can be analytically differentiated, e.g., for index reduction needed for mechanical systems. + +[[annotation:late-inline,Annotation LateInline]] +Annotation LateInline:: ++ +[source,modelica] +---- +/*literal*/ constant Boolean LateInline; +---- ++ +Has only an effect within a function declaration. ++ +If `LateInline = true`, the model developer proposes to inline the function after all symbolic transformations have been performed. ++ +[NOTE] +Late inlining is especially useful for differentiation and inversion of functions; for efficiency reasons it is then useful to replace all function calls with identical input arguments by one function call, before the inlining. ++ +If `LateInline = false`, the model developer proposes to not inline the function after symbolic transformations have been performed. ++ +The default for late inlining is tool-specific. +In particular, tools may automatically delay inlining in order to take advantage of function annotations for derivatives and inverses. ++ +`Inline = true, LateInline = false` is identical to `Inline = true`. ++ +`Inline = true, LateInline = true` is identical to `LateInline = true`. ++ +`Inline = false, LateInline = true` is identical to `LateInline = true`. ++ +[NOTE] +This annotation is for example used in `Modelica.Media.Water.IF97_Utilities.T_props_ph` to provide in combination with common subexpression elimination the automatic caching of function calls. +Furthermore, it is used in order that a tool is able to propagate specific enthalpy over connectors in the `Modelica.Fluid` library. + +[[annotation:inline-after-index-reduction,Annotation InlineAfterIndexReduction]] +Annotation InlineAfterIndexReduction:: ++ +[source,modelica] +---- +/*literal*/ constant Boolean InlineAfterIndexReduction; +---- ++ +Has only an effect within a function declaration. ++ +If `true`, the model developer proposes to inline the function after the function is differentiated for index reduction, and before any other symbolic transformations are performed. ++ +The default is to not perform this specific inlining. +This annotation cannot be combined with annotations `Inline` and `LateInline`. + +[[annotation:generate-events,Annotation GenerateEvents]] +Annotation GenerateEvents:: ++ +[source,modelica] +---- +/*literal*/ constant Boolean GenerateEvents; +---- ++ +Has only an effect within a function declaration. ++ +By default, `GenerateEvents = false` and expressions in the function body that would normally be event-generating shall not generate events, similar to inlining the function body while wrapping all expressions in `noEvent`, see <>. +By specifying `GenerateEvents = true`, event-generating expressions in the function body shall generate events as normal, similar to inlining the function body without wrapping all expressions in `noEvent`. +Having `GenerateEvents = true` implies `Inline = true` unless overridden by specifying one of the inlining annotations with value `true` (in particular, `GenerateEvents = true` cannot be combined with `Inline = false`). ++ +[NOTE] +-- +In case a given inlining annotation proposes to inline at a stage when the tool cannot process `GenerateEvents = true`, it is recommended to give a diagnostic and instead perform inlining of the function at the nearest stage where `GenerateEvents = true` can still be respected. + +If the function is called in a context where events will not be generated (e.g., inside another function without `GenerateEvents = true`) no special action is needed. +-- + +=== External Function Interface + +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. ++ +[NOTE] +Binding equations are executed prior to calling the external function. + +The format of an external function declaration is as follows. + +[source,grammar] +---- +function IDENT description-string + { component-clause ";" } + [ protected { component-clause ";" } ] +external [ language-specification ] + [ external-function-call ] + [ annotation-clause ] ";" + [ annotation-clause ";" ] +end IDENT; +---- + +Just as for any other function, components in the public part of an external function declaration shall be declared either as `input` or `output`. + +Protected components can be passed to the external function without being initialized by means of a declaration equation, which is useful for passing workspace memory to functions with FORTRAN style memory management, and the reason for passing them in the same (writable) way as output components (see <>). +The value of a protected component passed to the external function should be considered undefined (destroyed) after the external function call. + +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"`. + +[NOTE] +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 <>. +The external function call mechanism for `"builtin"` functions is implementation-defined. + +[NOTE] +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] +==== +Example: + +[source,modelica] +---- +package Modelica + package Math + function sin + input Real x; + output Real y; + external "builtin" + y = sin(x); + end sin; + end Math; +end Modelica; + +model UserModel + parameter Real p = Modelica.Math.sin(2); +end UserModel; +---- +==== + +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 constant expressions, 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. + +==== Argument type Mapping + +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` calls are passed as inputs. + +===== Simple Types + +Arguments of *simple* types are by default mapped as follows for C: + +[cols="a,a,a",options=autowidth] +|=== +|Modelica |C Input |C 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'`) and are encoded using UTF-8 (assuming `CHAR_BIT==8` in C) to facilitate calling of C functions. +The valid return values for an external function returning a `String` are: + +* A string given as `String` input to the external function. + +* A pointer to a C string literal. + +* A pointer returned by one of the string allocation functions in <>. + +[NOTE] +The reason why it is not allowed to return a string allocated with, for instance, `malloc` is that there is no transfer of ownership when a string is returned from the external function. +The external code would remain the owner of such a string, and would be responsible for eventually releasing the memory at some point. +Consequently, the Modelica simulation environment would not be able to assume that only its own string deallocation routines could invalidate any of the strings returned by external functions. + +`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`. + +[NOTE] +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: + +[cols="a,a,a",options=autowidth] +|=== +|Modelica |FORTRAN 77 Input |FORTRAN 77 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. +String are UTF-8 encoded, even though the support for non-ASCII characters in FORTRAN 77 is unclear and it is not relevant for the LAPACK/BLAS-routines. +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 ith enumeration literal is mapped to integer value i, 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. + +===== Arrays + +Unless an explicit function call is present in the `external`-clause, 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. + +[NOTE] +The type `size_t` is a C unsigned integer type. + +Arrays are stored in row-major order when calling C functions and in column-major order when calling FORTRAN 77 functions. + +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 <> or a record type as defined in <> and it is mapped to the type `T'` as defined in these sections for input arguments. +Array inputs to C-functions are const-pointers, indicating that the arrays shall not be changed. + +[cols="a,a,a",options=autowidth] +|=== +|Modelica |C Input |C Output + +|`T[dim1]` |`const T' *, size_t dim1` |`T' *, size_t dim1` +|`T[dim1, dim2]` |`const T' *, size_t dim1, size_t dim2` |`T' *, size_t dim1, size_t dim2` +|`T[..., dimn]` |`const T' *, ..., size_t dimn` |`T' *, ..., size_t dimn` +|=== + +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 <> and it is mapped to the type `T'` as defined in that section. + +[cols="a,a",options=autowidth] +|=== +|Modelica |FORTRAN 77 Input and output + +|`T[dim1]` |`T', INTEGER dim1` +|`T[dim1, dim2]` |`T', INTEGER dim1, INTEGER dim2` +|`T[dim1, ..., dimn]` |`T', INTEGER dim1, ..., INTEGER dimn` +|=== + +[example] +==== +Example: The following two examples illustrate the default mapping of array arguments to external C and FORTRAN 77 functions. + +[source,modelica] +---- +function foo + input Real a[:,:,:]; + output Real x; + external; +end foo; +---- + +The corresponding C prototype is as follows: + +[source,c] +---- +double foo(const double *, size_t, size_t, size_t); +---- + +If the external function is written in FORTRAN 77, i.e.: + +[source,modelica] +---- +function foo + input Real a[:,:,:]; + output Real x; +external "FORTRAN 77"; +end foo; +---- + +the default assumptions correspond to a FORTRAN 77 function defined as follows: + +[source,fortran] +---- +FUNCTION foo(a, d1, d2, d3) + DOUBLE PRECISION(d1, d2, d3) a + INTEGER d1 + INTEGER d2 + INTEGER d3 + DOUBLE PRECISION foo + ... +END +---- +==== + +When an explicit call to the external function is present, the array and the sizes of its dimensions must be passed explicitly. + +[example] +==== +Example: This example shows how arrays can be passed explicitly to an external FORTRAN 77 function when the default assumptions are unsuitable. + +[source,modelica] +---- +function foo + input Real x[:]; + input Real y[size(x,1),:]; + input Integer i; + output Real u1[size(y,1)]; + output Integer u2[size(y,2)]; +external "FORTRAN 77" + myfoo(x, y, size(x,1), size(y,2), u1, i, u2); +end foo; +---- + +The corresponding FORTRAN 77 subroutine would be declared as follows: + +[source,fortran] +---- +SUBROUTINE myfoo(x, y, n, m, u1, i, u2) + DOUBLE PRECISION(n) x + DOUBLE PRECISION(n,m) y + INTEGER n + INTEGER m + DOUBLE PRECISION(n) u1 + INTEGER i + DOUBLE PRECISION(m) u2 + ... +END +---- +==== + +===== Records + +Mapping of record types is only supported for C. +A Modelica record class is mapped as follows: + +* The record class is represented by a struct in C. + +* Each component of the Modelica record is mapped to its corresponding C representation. + A nested record component is mapped to a nested struct component. + +* The components 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). + +[example] +==== +Example: + +[source,modelica] +---- +record A + Integer b; +end A; +record R + Real x; + Real z; + A a1, a2; +end R; +---- + +is mapped to: + +[source,c] +---- +struct A { + int b; +}; +struct R { + double x; + double z; + struct A a1, b2; +}; +---- +==== + +==== Return Type Mapping + +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. +Otherwise the external function is assumed not to return anything; i.e., it is really a procedure or, in C, a void-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. +See <> regarding returning of `String` values. + +[NOTE] +In the case of an external function not returning anything, argument type mapping according to <> is performed in the absence of any explicit external function call. + +Return types are by default mapped as follows for C and FORTRAN 77: + +[cols="a,a,a",options=autowidth] +|=== +|Modelica |C |FORTRAN 77 + +|`Real` |`double` |`DOUBLE PRECISION` +|`Integer` |`int` |`INTEGER` +|`Boolean` |`int` |`LOGICAL` +|`String` |`const char*` |Not allowed +|`T[dim1, ..., dimn]` |Not allowed |Not allowed +|Enumeration type |`int` |`INTEGER` +|Record |See <> |Not allowed +|=== + +The element type `T` of an array can be any simple type as defined in <> or, for C, a record type is returned as a value of the record type defined in <>. + +==== Aliasing + +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] +==== +Example: + +[source,modelica] +---- +function foo + input Real x; + input Real y; + output Real z = x; +external "FORTRAN 77" + myfoo(x, y, z); +end foo; +---- +The following Modelica function: +[source,modelica] +---- +function f + input Real a; + output Real b; +algorithm + b := foo(a, a); + b := foo(b, 2 * b); +end f; +---- +can on most systems be transformed into the following C function: +[source,c] +---- +double f(double a) { + extern void myfoo_(double*, double*, double*); + double b, temp1, temp2; + + myfoo_(&a, &a, &b); + temp1 = 2 * b; + temp2 = b; + myfoo_(&b, &temp1, &temp2); + + return temp2; +} +---- + +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: +[source,modelica] +---- +function foo + input Real x; + protected Real xtemp = x; // Temporary used because myfoo changes its input + public input Real y; + output Real z; +external "FORTRAN 77" + myfoo(xtemp, y, z); +end foo; +---- + +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: +[source,c] +---- +temp1 = a; /* Temporary to avoid aliasing */ +myfoo_(&a, &temp1, &b); +---- + +The use of the function `foo` in Modelica is uninfluenced by these considerations. +==== + +==== Annotations for External Functions + +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="insertedCode")`, used to insert function prototypes or definitions needed for calling the external function in the code generated by the Modelica compiler. +When generating a call to the external function, the `"insertedCode"` shall be present at the top level somewhere before the point of the call (similar to where include directives are typically placed). +The `Include` annotation shall be used in such a way that each external function can be handled in a separate translation unit. +In particular, different external functions must not have `Include` annotations providing exported definitions of the same function symbol to avoid linking errors. ++ +A deprecated feature is that if multiple `Include` annotations -- possibly coming from different external functions -- have identical content, the tool shall not include this content more than once in any translation unit. +In case calls to several external functions are generated in the same translation unit, the `Include` annotations of the different functions must not define the same function -- except when relying on the deprecated behavior. ++ +The included code should be valid C89 code. +If the `external-function-call` contains any `size`-expression, the tool is responsible for ensuring that a C-header defining `size_t` is included before the `"insertedCode"`. +The `"insertedCode"`, conditionally preceded by a header for `size_t`, must be a valid translation unit. ++ +When an `Include` annotation is present, it shall provide a prototype for the external function, and hence the tool shall not produce an automatically generated prototype in the generated code in this case. ++ +Although all pointer types are const pointers in the type mapping for input arguments, it is a deprecated feature that the prototype in an `Include` annotation may use non-const pointers instead. ++ +[NOTE] +-- +For an external function declaration calling the external function `myfoo`, examples of `"insertedCode"` include: + +* An `#include` directive including a header file with a prototype for `myfoo`. + +* An `#include` directive including a source file with a `static` definition of `myfoo`. +Include guards should be used (either in the `Include` annotation or in the source file) to avoid relying on the deprecated feature that tools shall include at most one copy in the same translation unit. +(Having a `static` definition allows the same source file to be included by multiple `Include` annotations in different translation units.) + +* A prototype for `myfoo`. +This may be useful when no header file is available and it is not desirable to rely on the automatic generation of a prototype. + +* A piece of C code directly defining `myfoo`. +Since no other `Include` annotation is expected to contain a definition of `myfoo`, it is not necessary to make the definition `static`. +-- + +* The `annotation(IncludeDirectory="modelica:/TopPackage/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 <>. + +* The `annotation(LibraryDirectory="modelica:/TopPackage/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 <>. +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:/TopPackage/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 <>. +It is not specified how they are built. + +* The `annotation(License="modelica:/TopPackage/Resources/Licenses/MyLicense.txt")`, gives the license text file for the function. +It is analogous to the `License` annotation for a top-level class, see <>. + +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. + +[NOTE] +A tool may give a diagnostic 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 `TopPackage` used for `IncludeDirectory`, `LibraryDirectory`, and `SourceDirectory` indicates the top-level class where the annotation is found in the Modelica source code. + +[example] +==== +Example: Use of external functions and of object libraries: + +[source,modelica] +---- +package ExternalFunctions + model Example + Real x(start = 1.0), y(start = 2.0); + equation + der(x) = -ExternalFunc1(x); + der(y) = -ExternalFunc2(y); + end Example; + + model OtherExample + Real x(start = 1.0); + equation + der(x) = -ExternalFunc3(x); + end OtherExample; + + function ExternalFunc1 "Include header file for library implementation" + input Real x; + output Real y; + external "C" + y = ExternalFunc1_ext(x) + annotation(Library = "ExternalLib1", + Include = "#include \"ExternalFunc1.h\"", + SourceDirectory = + "modelica:/ExternalFunctions/Resources/Source"); + // The specified SourceDirectory is the default and thus redundant. + end ExternalFunc1; + + function ExternalFunc2 "Include header file for library implementation" + input Real x; + output Real y; + external "C" + annotation(Library = "ExternalLib2", + Include = "#include \"ExternalFunc2.h\""); + end ExternalFunc2; + + function ExternalFunc3 "Include source file" + input Real x; + output Real y; + external "C" + annotation(Include = "#include \"ExternalFunc3.c\""); + end ExternalFunc3; +end ExternalFunctions; + +package MyExternalFunctions + extends ExternalFunctions; +end MyExternalFunctions; +---- + +Directory structure: + +[listing] +---- +ExternalFunctions + package.mo -- Modelica code from above + Resources + Include -- Include files + ExternalFunc1.h -- C header file + ExternalFunc2.h -- C header file + ExternalFunc3.c -- C source file (not ideal) + Library -- Object libraries for different platforms + win32 + ExternalLib1.lib -- Static link library for VisualStudio + ExternalLib2.lib -- statically linking the dynamic link library + ExternalLib2.dll -- Dynamic link library (with manifest) + linux32 + libExternalLib1.a -- Static link library + libExternalLib2.so -- Shared library + Source -- Sources for library + Func1.c -- C source for ExternalLib1.lib + Func2.c -- C source for ExternalLib2.lib + HelperFunc.c -- C source also included in ExternalLib2.lib +MyExternalFunctions + package.mo +---- + +Note that calling the function `MyExternalFunctions.ExternalFunc1` will use the header and library files from `ExternalFunction`, the `ExternalFunctions.Example` will not use ExternalFunc3.c, and one library file may contain multiple functions. + +The C-source ExternalFunc3.c will be included fully, and is not part of any library. +That is not ideal for C-code, but it works for small functions. + +It is not specified how the C-sources in the specified `SourceDirectory` will be used to build the libraries. + +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): + +[source,c] +---- +/* File ExternalFunc2.h */ +#ifndef EXTERNAL_FUNC2_H_ +#define EXTERNAL_FUNC2_H_ +#ifdef __cplusplus +extern "C" { +#endif +#ifdef _MSC_VER +#ifdef EXTERNAL_FUNCTION_EXPORT +# define EXTLIB2_EXPORT __declspec( dllexport ) +#else +# define EXTLIB2_EXPORT __declspec( dllimport ) +#endif +#elif __GNUC__ >= 4 + /* In gnuc, all symbols are by default exported. It is still often useful, + to not export all symbols but only the needed ones */ +# define EXTLIB2_EXPORT __attribute__ ((visibility("default"))) +#else +# define EXTLIB2_EXPORT +#endif + +EXTLIB2_EXPORT double ExternalFunc2(double); + +#ifdef __cplusplus +} +#endif +#endif +---- +==== + +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. + +==== Examples + +===== Input Parameters, Function Value + +[example] +==== +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. + +[source,modelica] +---- +function foo + input Real x; + input Integer y; + output Real w; + external; +end foo; +---- + +This corresponds to the following C prototype: + +[source,c] +---- +double foo(double, int); +---- + +Example call in Modelica: + +[source,modelica] +---- +z = foo(2.4, 3); +---- +Translated call in C: +[source,c] +---- +z = foo(2.4, 3); +---- +==== + +===== Arbitrary Placement of Output Parameters, No External Function Value + +[example] +==== +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. + +[source,modelica] +---- +function foo + input Real x; + input Integer y; + output Real u1; + output Integer u2; +external "C" + myfoo(x, u1, y, u2); +end foo; +---- + +This corresponds to the following C prototype: + +[source,c] +---- +void myfoo(double, double *, int, int *); +---- + +Example call in Modelica: + +[source,modelica] +---- +(z1,i2) = foo(2.4, 3); +---- +Translated call in C: +[source,c] +---- +myfoo(2.4, &z1, 3, &i2); +---- +==== + +===== Both Function Value and Output Variable + +[example] +==== +Example: The following external function returns two results: one function value and one output parameter value. +Both are mapped to Modelica output parameters. + +[source,modelica] +---- +function foo + input Real x; + input Integer y; + output Real funcvalue; + output Integer out1; +external "C" + funcvalue = myfoo(x, y, out1); +end foo; +---- + +This corresponds to the following C prototype: + +[source,c] +---- +double myfoo(double, int, int *); +---- + +Example call in Modelica: + +[source,modelica] +---- +(z1,i2) = foo(2.4, 3); +---- + +Translated call in C: + +[source,c] +---- +z1 = myfoo(2.4, 3, &i2); +---- +==== + +==== Utility Functions + +This section describes the utility functions declared in `ModelicaUtilities.h`, which can be called in external Modelica functions written in C. + +The tool must ensure that the header is found by `#include "ModelicaUtilities.h"` within an `Include` annotation (see <>); no `IncludeDirectory` annotation is needed. + +[example] +==== +Example: The following usage patterns are common: + +* The `Include` annotation may first `#include "ModelicaUtilities.h"`, and then contain the external function definition where the utility functions may be used. + +* Like above, but moving the annotation content above to, say, myExtFun.c, and then just do `#include "myExtFun.c"` in the annotation. + +* After `#include "ModelicaUtilities.h"`, the `Include` annotation can also define a wrapper around a function in a linked library that does the real job, where the wrapper forwards the arguments as well as passes function pointers for a selection of functions from `ModelicaUtilities.h`. +==== + +[NOTE] +When building external libraries independently of Modelica tools, it is not possible to rely on the tool mechanism that provides `ModelicaUtilities.h` for `Include` annotations. +Instead, an external library project may contain its own instance of `ModelicaUtilities.h`, for example obtained by making a copy of the instance included within the Modelica Standard Library. + +===== Error Reporting Utility Functions + +The functions listed below produce a message in different ways. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`ModelicaMessage(string)` |Message with fixed string .3+.^|<> +|`ModelicaWarning(string)` |Warning with fixed string +|`ModelicaError(string)` |Error with fixed string +|`ModelicaFormatMessage(format, ...)` |Message with printf style formatting .3+.^|<> +|`ModelicaFormatWarning(format, ...)` |Warning with printf style formatting +|`ModelicaFormatError(format, ...)` |Error with printf style formatting +|`ModelicaVFormatMessage(format, ap)` |Message with vprintf style formatting .3+.^|<> +|`ModelicaVFormatWarning(format, ap)` |Warning with vprintf style formatting +|`ModelicaVFormatError(format, ap)` |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. + +[[function:ModelicaMessage-et-al,Function ModelicaMessage et al.]] +Function ModelicaMessage, ModelicaWarning, ModelicaError:: ++ +[source,c] +---- +void ModelicaMessage(const char* string); +void ModelicaWarning(const char* string); +void ModelicaError(const char* string); +---- ++ +Output the fixed message string (no format control). + +[[function:ModelicaFormatMessage-et-al,Function ModelicaFormatMessage et al.]] +Function ModelicaFormatMessage, ModelicaFormatWarning, ModelicaFormatError:: ++ +[source,c] +---- +void ModelicaFormatMessage(const char* format, ...); +void ModelicaFormatWarning(const char* format, ...); +void ModelicaFormatError(const char* format, ...); +---- ++ +Output the message under the same format control as the C function `printf`. + +[[function:ModelicaVFormatMessage-et-al,Function ModelicaVFormatMessage et al.]] +Function ModelicaVFormatMessage, ModelicaVFormatWarning, ModelicaVFormatError:: ++ +[source,c] +---- +void ModelicaVFormatMessage(const char* format, va_list ap); +void ModelicaVFormatWarning(const char* format, va_list ap); +void ModelicaVFormatError(const char* format, va_list ap); +---- ++ +Output the message under the same format control as the C function `vprintf`. + +===== String Allocation Utility Functions + +The functions listed below are related to string allocation. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`ModelicaAllocateString(len)` |Allocate or error |<> +|`ModelicaAllocateStringWithErrorReturn(len)` |Allocate or null |<> +|`ModelicaDuplicateString(str)` |Duplicate or error |<> +|`ModelicaDuplicateStringWithErrorReturn(str)` |Duplicate or null |<> +|=== + +As described in <>, an external function wanting to return a newly constructed string must allocate this string with one of the string allocation functions in this section. +The allocated memory is owned by the Modelica simulation environment, and may only be accessed by the external function during the currently executing external function call. +The string allocation functions can also be used to allocate temporary strings that are not returned from the external function, with the convenience of the Modelica simulation environment being responsible for deallocation after the return of the external function. +(This is particularly convenient for avoiding memory leaks in the event of abnormal termination of the external function, for example, via `ModelicaError`.) + +[NOTE] +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 <>, may be allocated with the standard C mechanisms, like `malloc`. + +[[function:ModelicaAllocateString,Function ModelicaAllocateString]] +Function ModelicaAllocateString:: ++ +[source,c] +---- +char* ModelicaAllocateString(size_t len); +---- ++ +Allocates len+1 characters, and sets the last one to NUL. +If an error occurs, this function does not return, but calls `ModelicaError`. + +[[function:ModelicaAllocateStringWithErrorReturn,Function ModelicaAllocateStringWithErrorReturn]] +Function ModelicaAllocateStringWithErrorReturn:: ++ +[source,c] +---- +char* ModelicaAllocateStringWithErrorReturn(size_t len); +---- ++ +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. + +[[function:ModelicaDuplicateString,Function ModelicaDuplicateString]] +Function ModelicaDuplicateString:: ++ +[source,c] +---- +char* ModelicaDuplicateString(const char* str); +---- ++ +Returns a writeable duplicate of the NUL-terminated string str. +If an error occurs, this function does not return, but calls `ModelicaError`. + +[[function:ModelicaDuplicateStringWithErrorReturn,Function ModelicaDuplicateStringWithErrorReturn]] +Function ModelicaDuplicateStringWithErrorReturn:: ++ +[source,c] +---- +char* ModelicaDuplicateStringWithErrorReturn(const char* str); +---- ++ +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. + +==== External Objects + +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`. ++ +[NOTE] +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`, may extend from empty classes (<>), but not contain any other elements. +The functions `constructor` and `destructor` shall not be replaceable. +It is not legal to call the `constructor` and `destructor` functions explicitly. + +* The `constructor` function is called exactly once before the first use of the object. +The `constructor` shall have exactly one output argument in which the constructed instance derived from `ExternalObject` is returned. +The arguments to the constructor must not -- directly nor indirectly -- depend on the external object being constructed. +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). +To indicate failure, the `constructor` may return a null pointer, to be treated in the same way as a failed `assert` in Modelica. ++ +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. + +* For each completely constructed object, the `destructor` is called exactly once, after the last use of the object, even if an error occurs. +The `destructor` shall have no output arguments and the only input argument of the destructor shall be of the type derived from `ExternalObject`. +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). ++ +[NOTE] +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. ++ +[NOTE] +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`. ++ +[NOTE] +Apart from empty classes (<>), 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. ++ +[NOTE] +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] +==== +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): + +[source,modelica] +---- +class MyTable + extends ExternalObject; + function constructor + input String fileName = ""; + input String tableName = ""; + output MyTable table; + external "C" + table = initMyTable(fileName, tableName); + end constructor; + + function destructor "Release storage of table" + input MyTable table; + external "C" + closeMyTable(table); + end destructor; +end MyTable; +---- +and used in the following way: +[source,modelica] +---- +model test "Define a new table and interpolate in it" + MyTable table=MyTable(fileName ="testTables.txt", + tableName="table1"); // call initMyTable + Real y; +equation + y = interpolateMyTable(table, time); +end test; +---- + +This requires to provide the following Modelica function: + +[source,modelica] +---- +function interpolateMyTable "Interpolate in table" + input MyTable table; + input Real u; + output Real y; +external "C" + y = interpolateMyTable(table, u); +end interpolateTable; +---- + +The external C functions may be defined in the following way: + +[source,c] +---- +typedef struct { /* User-defined datastructure of the table */ + double* array; /* nrow*ncolumn vector */ + int nrow; /* number of rows */ + int ncol; /* number of columns */ + int type; /* interpolation type */ + int lastIndex; /* last row index for search */ +} MyTable; + +void* initMyTable(const char* fileName, const char* tableName) { + MyTable* table = malloc(sizeof(MyTable)); + if ( table == NULL ) ModelicaError("Not enough memory"); + // read table from file and store all data in *table + return (void*) table; +} + +void closeMyTable(void* object) { /* Release table storage */ + MyTable* table = (MyTable*) object; + if ( object == NULL ) return; + free(table->array); + free(table); +} + +double interpolateMyTable(void* object, double u) { + MyTable* table = (MyTable*) object; + double y; + // Interpolate using "table" data (compute y) + return y; +} +---- +==== \ No newline at end of file diff --git a/docs/13__packages.adoc b/docs/13__packages.adoc new file mode 100644 index 000000000..36fb6c6cd --- /dev/null +++ b/docs/13__packages.adoc @@ -0,0 +1,479 @@ +== Packages +:id: packages + +Packages in Modelica may contain definitions of constants and classes including all kinds of specialized classes, functions, and subpackages. +By the term _subpackage_ we mean that the package is declared inside another package, no inheritance relationship is implied. +Parameters and variables cannot be declared in a package. +The definitions in a package should typically be related in some way, which is the main reason they are placed in a particular package. +Packages are useful for a number of reasons: + +* Definitions that are related to some particular topic are typically grouped into a package. This makes those definitions easier to find and the code more understandable. + +* Packages provide encapsulation and coarse-grained structuring that reduces the complexity of large systems. An important example is the use of packages for construction of (hierarchical) class libraries. + +* Name conflicts between definitions in different packages are eliminated since the package name is implicitly prefixed to names of definitions declared in a package. + +* Information hiding and encapsulation can be supported to some extent by declaring `protected` classes, types, and other definitions that are available only inside the package and therefore inaccessible to outside code. + +* Modelica defines a method for locating a package by providing a standard mapping of package names to storage places, typically file or directory locations in the file system. + +=== Package as Specialized Class + +The package concept is a specialized class (see <>), using the keyword `package`. + +=== Importing Definitions from a Package + +The `import` clause makes public classes and other public definitions declared in some package available for use by shorter names in a class or a package. +It is the only way of referring to definitions declared in some other package for use inside an encapsulated package or class. + +[NOTE] +-- +The `import` clauses in a package or class fill the following two needs: + +* Making definitions from other packages available for use (by shorter names) in a package or class. + +* Explicit declaration of usage dependences on other packages. +-- + +An `import` clause can occur in one of the following syntactic forms: + +[source,modelica] +---- +import definitionname; // qualified import of top-level definition +import packagename.definitionname; // qualified import +import packagename.{def1, def2, ..., defn}; // multiple definition import +import packagename.*; // unqualified import +import shortname = definitionname; // renaming import of top-level definition +import shortname = packagename.definitionname; // renaming import +---- + +Here `packagename` is the fully qualified name of the imported package including possible dot notation and `definitionname` is the name of an element in a package. +The multiple definition import is equivalent to multiple single definition imports with corresponding `packagename` and definition names. + +==== Lookup of Imported Names + +This section only defines how the imported name is looked up in the `import` clause. +For lookup in general -- including how `import` clauses are used -- see <>. + +Lookup of the name of an imported package or class deviates from the normal lexical lookup. + +For example, consider `A.B.C` in the import clauses `import A.B.C;`, `import D = A.B.C;`, or `import A.B.C.*;`. +Here, lookup starts with the lexical lookup of the first part of the name (`A`) at the top level. + +Qualified import clauses may only refer to packages or elements of packages, i.e., in `import A.B.C;` or `import D = A.B.C;`, `A.B` must be a package. +Unqualified import clauses may only import from packages, i.e., in `import A.B.*;`, `A.B` must be a package. + +[NOTE] +In `import A;` the class `A` can be any class which is an element of the unnamed top-level package. + +[example] +==== +For example, if the package `ComplexNumbers` would have been declared as a subpackage inside the package `Modelica.Math`, its fully qualified name would be `Modelica.Math.ComplexNumbers`. +`definitionname` is the simple name without dot notation of a single definition that is imported. +A `shortname` is a simple name without dot notation that can be used to refer to the package after import instead of the presumably much longer `packagename`. + +The forms of `import` are exemplified below assuming that we want to access the addition operation of the hypothetical package `Modelica.Math.ComplexNumbers`: + +[source,modelica] +---- +import Modelica.Math.ComplexNumbers; // Accessed by ComplexNumbers.Add +import Modelica.Math.ComplexNumbers.Add; // Accessed by Add +import Modelica.Math.ComplexNumbers.{Add,Sub}; // Accessed by Add and Sub +import Modelica.Math.ComplexNumbers.*; // Accessed by Add +import Co = Modelica.Math.ComplexNumbers; // Accessed by Co.Add +---- +==== + +==== Rules for Import Clauses + +The following rules apply to `import` clauses: + +* The `import` clauses are _not_ inherited. + +* The `import` clauses are not named elements of a class or package. This means that `import` clauses cannot be changed by modifiers or redeclarations. + +* The _order_ of `import` clauses does not matter. + +* One can only import _from_ packages, not from other kinds of classes. Both packages and classes can be imported _into_, i.e., they may contain `import` clauses. + +* An imported package or definition should always be referred to by its fully qualified name in the `import` clause. + +* Multiple qualified `import` clauses shall not have the same import name (see <>). + +=== The Modelica Library Path - MODELICAPATH + +The top-level scope implicitly contains a number of classes stored externally. +If a top-level name is not found at global scope, a Modelica translator shall look up additional classes in an ordered list of library roots, called `MODELICAPATH`. + +[NOTE] +The implementation of `MODELICAPATH` is tool dependent. +In order that a user can work in parallel with different Modelica tools, it is advisable to not have this list as environment variable, but as a setting in the respective tool. +Since `MODELICAPATH` is tool dependent, it is not specified in which way the list of library roots is stored. +Typically, on a Windows system `MODELICAPATH` is a string with path names separated by `;` whereas on a Linux system it is a string with path names separated by a `:`. + +In addition a tool may define an internal list of libraries, since it is in general not advisable for a program installation to modify global environment variables. +The version information for a library (as defined in <>) may also be used during this search to search for a specific version of the library (e.g., if Modelica library version 2.2 is needed and the first directory in `MODELICAPATH` contain Modelica library version 2.1, whereas the second directory contains Modelica version 2.2, then Modelica library version 2.2 is loaded from the second directory.). + +[NOTE] +The first part of the path `A.B.C` (i.e., `A`) is located by searching the ordered list of roots in `MODELICAPATH`. +If no root contains `A` the lookup fails. +If `A` has been found in one of the roots, the rest of the path is located in `A`; if that fails, the entire lookup fails without searching for `A` in any of the remaining roots in `MODELICAPATH`. + +If during lookup a top-level name is not found in the unnamed top-level scope, the search continues in the package hierarchies stored in these directories. + +[example] +==== +Example: Assume `MODELICAPATH = C:\library;C:\lib1;C:\lib2`, with three directories containing the roots of the package hierarchies `Modelica`, `MyLib`, and `ComplexNumbers`. +The first two are represented as the subdirectories `C:\library\Modelica` and `C:\lib1\MyLib`, whereas the third is stored as the file `C:\lib2\ComplexNumbers.mo`. + +.Roots of package hierarchies, e.g., `Modelica`, `MyLib`, and `ComplexNumbers` in `MODELICAPATH = C:\library;C:\lib1;C:\lib2`. +image::media/modelicapath.svg[] + +Assume that we want to access the package `MyLib.Pack2` in the figure above, e.g., through an import clause `import MyLib.Pack2;`. +During lookup we first try to find a package `MyLib` corresponding to the first part of the name in the import-statement. +It is not found in the top-level scope since it has not previously been loaded into the environment. + +Since the name was not found in the top-level scope the search continues in the directories in the `MODELICAPATH` in the specified order. +For the search to succeed, there must be a subdirectory `MyLib` or a file `MyLib.mo` in one of the directories mentioned in the `MODELICAPATH`. +If there is no such subdirectory or file, the lookup fails. +If `MyLib` is found in one of the directories, the rest of the name, in this case `Pack2`, is located in `MyLib`. +If that fails, the entire lookup fails without continuing the search in possibly remaining directories. + +In this example the name matches the subdirectory named `MyLib` in the second directory `C:\lib1` mentioned in the `MODELICAPATH`. +This subdirectory must have a file `package.mo` containing a definition of the package `MyLib`, according to the Modelica rules on how to map a package hierarchy to the file system. +The subpackage `Pack2` is stored in its own subdirectory or file in the subdirectory `MyLib`. +In this case the search succeeds and the package `MyLib.Pack2` is loaded into the environment. +==== + +=== File System Mapping of Package/Class + +A package/class hierarchy may be represented in the hierarchical structure of the operating system (the file system). +For classes with version information see also <>. +The nature of such an external entity falls into one of the following two groups: + +* Directory in the file system. + +* File in the file system. + +Each Modelica file in the file system is stored in UTF-8 format (defined by The Unicode Consortium; https://unicode.org). +A deprecated feature is that the file may start with the UTF-8 encoded BOM (byte order mark; `0xef 0xbb 0xbf`); this is treated as white-space in the grammar. +Since the use of BOM is deprecated, tools can ignore any BOM when reading, and it is recommended to never write it. + +[NOTE] +Tools may also store classes in data-base systems, but that is not standardized. + +==== Directory Hierarchy Mapping + +A directory shall contain a node, the file `package.mo`. +The node shall contain a `stored-definition` that defines a class `A` with a name matching the name of the structured entity. + +[NOTE] +The node typically contains documentation and graphical information for a package, but may also contain additional elements of the class `A`. + +A directory may also contain one or more sub-entities (directories or files). +The sub-entities are mapped as elements of the class defined by their enclosing structured entity. +Two sub-entities shall not define classes with identical names. + +[example] +==== +Example: If directory `A` contains the three files `package.mo`, `B.mo` and `C.mo`, the classes defined are `A`, `A.B`, and `A.C`. +==== + +[example] +==== +Example: A directory shall not contain both the sub-directory `A` and the file `A.mo`. +==== + +In order to preserve the order of sub-entities it is advisable to create a file `package.order` where each line contains the name of one class or constant (using its Modelica `IDENT` form). +If a `package.order` is present when reading a structured entity the classes and constants are added in this order; if the contents does not exactly match the classes and constants in the package, the resulting order is tool specific and a warning may be given. +Classes and constants that are stored in `package.mo` are also present in `package.order` but their relative order should be identical to the one in `package.mo` (this ensures that the relative order between classes and constants stored in different ways is preserved). + +==== Single File Mapping + +When mapping a package or class hierarchy to a file (e.g., the file `A.mo`), the file content shall match `stored-definition` in the grammar. +In this case, the `stored-definition` shall only define a single class whose name (here, `A`) matches the name of the nonstructured entity. +The filename shall have the extension `.mo`. + +==== The within Clause + +A `within` clause has the following syntax: + +[source,grammar] +---- +within [ packageprefixname ] ; +---- + +A non-top-level entity shall begin with a `within` clause which for the class defined in the entity specifies the location in the Modelica class hierarchy. +A top-level class may contain a `within` clause with no name. +For a sub-entity of an enclosing structured entity, the `within` clause shall designate the class of the enclosing entity; and this class must exist and must not have been defined using a short class definition. +See <> regarding the use of `within` clause when a `stored-definition` does not hold exactly one class definition. + +[example] +==== +Example: The subpackage `Rotational` declared within `Modelica.Mechanics` has the fully qualified name `Modelica.Mechanics.Rotational`, which is formed by concatenating the `packageprefixname` with the short name of the package. +The declaration of `Rotational` could be given as below: + +[source,modelica] +---- +within Modelica.Mechanics; +package Rotational // Modelica.Mechanics.Rotational + ... +end Rotational; +---- +==== + +=== Stored Definitions Containing Multiple Class Definitions + +The `stored-definition` in the grammar allows for zero or more class definitions, but a `stored-definition` not containing exactly one class definition can only be used to define top-level classes, cannot be used for file system mapping of packages or class-hierarchies (see <>), and shall be ignored when searching the `MODELICAPATH` (see <>). +It follows that any `within` clause (see <>) in such a `stored-definition` shall not contain a `packageprefixname`. + +=== External Resources + +Examples of references to external resources include links and images in HTML documentation, and images in the `Bitmap` annotation (see <>). +Absolute URIs should be used, for example `file:///` and the URI scheme `modelica:/` which can be used to retrieve resources associated with a package. +According to the URI specification scheme names are case-insensitive, but the lower-case form should be used, that is `Modelica:/` is allowed but `modelica:/` is the recommended form. + +The Modelica-scheme has the ability to reference a hierarchical structure of resources associated with packages. +The same structure is used for all kind of resource references, independent of use (external file, image in documentation, bitmap in icon layer, and link to external file in the documentation), and regardless of the storage mechanism. + +Any Modelica-scheme URI containing a slash after the package-name is interpreted as a reference to a resource. +The first _segment_ of the _path_ of the URI is interpreted as a fully qualified package name and the rest of the _path_ of the URI is interpreted as the path (relative to the package) of the resource. +Each storage scheme can define its own interpretation of the path (but care should be taken when converting from one storage scheme or when restructuring packages that resource references resolve to the same resource). +Any storage scheme should be constrained such that a resource with a given path should be unique for any package name that precedes it. +The second segment of the path shall not be the name of a class in the package given by the first segment. + +As a deprecated feature the URI may start with `modelica://` and use the host-part of the authority as the fully qualified package name. +That feature is widely used, but deprecated since host-names are generally case-insensitive. + +[NOTE] +Examples of deprecated URIs would be `modelica://Modelica/Resources/C.jpg` (referring to a resource) and `modelica://Modelica.Blocks` (referring to a package). +These should be rewritten as `modelica:/Modelica/Resources/C.jpg` and `modelica:/Modelica.Blocks`. + +When Modelica packages are stored hierarchically in a file system (i.e., package `A` in a directory `A` containing `package.mo`) the resource +`modelica:/A/Resources/C.jpg` should be stored in the file `A/Resources/C.jpg`, it is not recommend to use `modelica:/A.B/C.jpg` for referencing resources; it could be stored in the file `A/B/C.jpg` -- which is counter-intuitive if `A.B` is stored together with `A`. +When Modelica packages are stored in other formats a similar mapping should be defined, such that a resource with a given path should be unique for any package name that precedes it. +The second segment of the path shall not be the name of a class in the package given by the first segment. +As above for `Modelica 3.2.1/package.mo`, i.e., resources starting from `Modelica 3.2.1`, and `modelica:/Modelica.Mechanics/C.jpg` is `Modelica 3.2.1/Mechanics/C.jpg` -- regardless of whether `Modelica.Mechanics` is stored in `Modelica 3.2.1/package.mo`, `Modelica 3.2.1/Mechanics.mo`, or `Modelica 3.2.1/Mechanics/package.mo`. + +When mapping a Modelica URI to a file system path, the file system path shall end in a directory separator if and only if the URI path ends in the segment separator `/`. +For example, if `modelica:/A/Resources` maps to `A/Resources`, then `modelica:/A/Resources/` maps to `A/Resources/`, and vice versa. + +[NOTE] +The use of a trailing segment separator is recommended when the resource is a directory and the file system path will be prepended to relative file paths within the directory. +If possible, use URIs for specific files or specific sub-directories instead of appending relative paths to a generic URI such as `modelica:/A/Resources/` as the latter creates a dependency on the entire directory. + +For a Modelica-package stored as a single file, `A.mo`, the resource `modelica:/A/C.jpg` refers to a file `C.jpg` stored in the same directory as `A.mo`, but using resources in this variant is not recommended since multiple packages will share resources. + +In case the name of the class contains quoted identifiers, the single-quote `'` and any reserved characters (`:`, `/`, `?`, `#`, `[`, `]`, `@`, `!`, `$`, `&`, `(`, `)`, `*`, `+`, `,`, `;`, `=`) should be percent-encoded as normal in URIs. + +[example] +==== +Example: Consider a top-level package `Modelica` and a class `Mechanics` inside it, a reference such as `modelica:/Modelica.Mechanics/C.jpg` is legal, while `modelica:/Modelica/Mechanics/C.jpg` is illegal. +The references `modelica:/Modelica.Mechanics/C.jpg` and `modelica:/Modelica/C.jpg` must also refer to two distinct resources. +==== + +=== Multilingual Descriptions + +[NOTE] +Descriptive texts in a model or library are usually formulated in English. +This section describes how a tool can present the library in another language. +Translated Modelica text is provided by external files, so that no modification of the original Modelica text is required. + +The texts in following Modelica constructs should be translated: + +* description strings of component declarations and classes + +* strings in the following annotations: + +** `Text.string`, `Text.textString` + +** `missingInnerMessage`, `obsolete`, `unassignedMessage` + +** `Dialog.group`, `Dialog.tab` + +** `Dialog.loadSelector.caption`, `Dialog.loadSelector.filter`, `Dialog.saveSelector.caption`, `Dialog.saveSelector.filter` + +** `Documentation.info`, `Documentation.revisions` + +** `Figure.title`, `Figure.caption`, `Figure.group`, `Plot.title`, `Axis.label`, `Curve.legend` + +** `mustBeConnected` + +[NOTE] +None of the translatable constructs can have any impact on simulation results. + +Comments (delimited as well as rest-of-line) are not translated. +Only constructs given entirely by one or more concatenated string literals are translated, using nothing but the operator `+` for concatenation. +In order to have parameter values as part of the texts the special substitution syntax is preferable (see <> and <>), and translators need to be aware of these substrings in order to make good translations. + +[example] +==== +Example: Consider: + +[source,modelica] +---- +annotation(..., Text(string = "1st Frequency: %f1"), + Text(string = "2nd Frequency: " + String(w2 / (2 * pi))), ...); +---- + +In this example only `"1st Frequency: %f1"` can be translated; the second `Text.string` doesn't consist entirely of concatenated string literals, and is hence completely excluded from translation. +==== + +The files to support translation must be provided along with the library. +They must be stored in the resources directory `modelica://LibraryName/Resources/Language/`. + +Two kind of files in Gettext format have to be provided: + +. Template file `LibraryName.pot` (Portable Object Template), one file per library which is stored as the resource `modelica://LibraryName/Resources/Language/LibraryName.pot`. +It describes all translatable strings in the library, but does not contain any translations. +The pattern `LibraryName` denotes the toplevel class name of the library. + +. One file for each supported language with the name `LibraryName.language.po` (Portable Object), as the resource `modelica://LibraryName/Resources/Language/LibraryName.language.po`. +This file is a copy of the associated template file, but extended with the translations in the specified language. +The pattern `language` stands for the ISO 639-1 language code, e.g., `de` or `sv`. + +The detailed format of these files is described in the Gettext Manual. +Use of translation files in other formats (including the binary MO file format) is not standardized in Modelica. +For Modelica translations, only the keywords `msgctxt`, `msgid` and `msgstr` are used, meaning that a translation entry looks like this: + +[source,text] +---- +#: filename:lineNumber +#, no-c-format +msgctxt messageContext +msgid messageIdentifier +msgstr messageTranslation +---- + +The restriction to a few keywords makes it easier for tools to support the format without relying on the implementation from <>. + +The use of `no-c-format` ensures that translation tools will not parse `"%class"` as the format specifier `%c` followed by _lass_. + +[NOTE] +-- +In the remainder of this section, several facts about the gettext specification are interleaved non-normatively for easy access to some of the gettext basics. +Always refer to the external gettext specification for full detail or in case of doubt. + +All text strings are in double quotes and encoded with UTF-8 characters. +Comments start with a `#` and are continued until the end of line. +Spaces outside strings are ignored and used as separators. + +The files consist of a header and a body. +The header is marked with an empty `msgid` and looks like this: + +[source,text] +---- +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-03-15 10:52+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +---- + +All general terms in the header should be replaced by specific information. + +Following the header, there is one translation entry for each string to be translated. +It can start with an optional comment describing the location (file name and line number) of the text to translate. +Multiple occurrences of the same string can be listed here, separated by space. +-- + +The `messageContext` following the keyword `msgctxt` shall be the full name of the Modelica class (e.g., `"Modelica.Blocks.Math.Sin"`) where the text appears. +Short class definitions do not appear here. +Texts in such classes belong to the enclosing full class definition. + +The text string which shall be translated is used as `messageIdentifier` (following the `msgid` keyword), and shall contain the original string from the Modelica code. +Note that if a `msgid` string is given more than once in the same context, all occurrences are translated with the same (last) translation! + +[NOTE] +The `messageTranslation` (following the keyword `msgstr`) is the translation of `messageIdentifier` and is typically edited using special tools for translators. +In the template file this string is empty by definition. +If this is empty in a language specific file the `messageIdentifier` may be used instead. + +[NOTE] +-- +Since in Modelica control sequences also start with a backslash and another backslash is used to use sequences literally or to hide double quotes, no change is required here. +But Modelica allows strings to go over more than one line, gettext does not. +Therefore, line breaks in Modelica must be encoded with `"\n"` for gettext. + +In order to avoid long lines in the translated strings (following `msgid` or `msgstr`), strings may be split into smaller parts given on consecutive lines. +E.g., the Modelica description + +[source,modelica] +---- +"A +B\"C" + "D\nE" +---- + +evaluates to: + +[source,text] +---- +A +B"CD +E +---- + +which in the translation entry looks like: + +[source,text] +---- +msgid "" +"A\n" +"B\"CD\n" +"E" +---- +-- + +[example] +==== +Example: Consider a simple sine-source: + +[source,modelica] +---- +within MyPackage.Sources; +model Sine "Sine" + parameter Frequency f=2 "Frequency"; + RealOutput y=sin(2*pi*f*time); // Relying on imported types + /* Could add details. Note that this is not translated */ + annotation(Icon(graphics={Text(extent={{0,0},{40,40}}, + textString="Frequency: %f")})); +end Sine; +---- + +The entries for translating this model into Swedish could look like this: + +[source,text] +---- +#: MyPackage/Sources/package.mo:10126 +#, no-c-format +msgctxt "MyPackage.Sources.Sine" +msgid "Sine" +msgstr "Sinus" +#: MyPackage/Sources/package.mo:10127 +#, no-c-format +msgctxt "MyPackage.Sources.Sine" +msgid "Frequency" +msgstr "Frekvens" +#: MyPackage/Sources/package.mo:10131 +#, no-c-format +msgctxt "MyPackage.Sources.Sine" +msgid "Frequency: %f" +msgstr "Frekvens: %f" +---- +==== + +[NOTE] +To support the translation of these strings a number of free and commercial tools exist in context of GNU gettext. \ No newline at end of file diff --git a/docs/14__overloaded.adoc b/docs/14__overloaded.adoc new file mode 100644 index 000000000..aba3ae305 --- /dev/null +++ b/docs/14__overloaded.adoc @@ -0,0 +1,462 @@ +== Overloaded Operators + +A Modelica `operator record` can overload the behavior for operations such as constructing, adding, multiplying etc. + +The overloading is defined in such a way that ambiguities are not allowed and give an error. +Furthermore, it is sufficient to define overloading for scalars. +Some overloaded array operations are automatically deduced from the overloaded scalar operations (see item 4 and item 3 in the lists below), and others can be defined independently of the corresponding scalar operations. + +=== Overview of Overloaded Operators + +In an `operator record` the definition of operations are done using the specialized class `operator` (a specialized class similar to `package`, see <>) followed by the name of the operation. +Each `operator` class is comprised of functions implementing different variants of the operation for the `operator record` class in which the definition resides. + +* Overloaded constructors, see <>: + +`'constructor'`, `'0'` + +* Overloaded string conversions, see <>: + +`'String'` + +* Overloaded binary operations, see <>: + +`'+'`, `'-'` (subtraction), `'*'`, `'/'`, `'^'`, +`'=='`, `'<='`, `'>'`, `'<'`, +`'>='`, `'<='`, `'and'`, `'or'` + +* Overloaded unary operations, see <>: + +`'-'` (negation), `'not'` + +The functions defined in the operator-class must take at least one component of the record class as input, except for the constructor-functions which instead must return one component of the record class. +All of the functions shall return exactly one output. + +The functions can be either called as defined in this section, or they can be called directly using the hierarchical name. +The operator or operator function must be encapsulated; this allows direct calls of the functions and prohibits the functions from using the elements of operator record class. + +The `operator record` may also contain additional functions, and declarations of components of the record. +It is not legal to extend from an `operator record`, except as a short class definition modifying the default attributes for the component elements directly inside the operator record. + +If an operator record was derived by a short class definition, the overloaded operators of this operator record are the operators that are defined in its base class, for subtyping see <>. + +The precedence and associativity of the overloaded operators is identical to the one defined in <> in <>. + +[NOTE] +==== +Note, the operator overloading as defined in this section is only a short hand notation for function calls. +==== + +=== Matching Function + +All functions defined inside the `operator` class must return one output (based on the restriction above), and may include functions with optional arguments, i.e., functions of the form + +[source,modelica] +---- +function f + input A₁ u₁; + ... + input Aₘ uₘ = aₘ; + ... + input Aₙ uₙ; + output B y; +algorithm + ... +end f; +---- + +The vector _P_ indicates whether argument _m_ of `f` has a default value (true for default value, false otherwise). +A call `f(a₁, a₂, ..., aₖ, b₁ = w₁, ..., bₚ = wₚ)` with distinct names _bⱼ_ is a valid match for the function `f`, provided (treating `Integer` and `Real` as the same type) + +* _Aᵢ_ = typeOf(_aᵢ_) for 1 ≤ _i_ ≤ _k_, +* the names _bⱼ_ = _uQⱼ_, _Qⱼ_ > _k_, _AQⱼ_ = typeOf(_wⱼ_) for 1 ≤ _j_ ≤ _p_, and +* if the union of {_i_: 1 ≤ _i_ ≤ _k_ }, {_Qⱼ_: 1 ≤ _j_ ≤ _p_}, and {_m_: _Pₘ_ and 1 ≤ _m_ ≤ _n_ } is the set {_i_: 1 ≤ _i_ ≤ _n_}. + +[NOTE] +==== +This corresponds to the normal treatment of function calls with named arguments, requiring that all inputs have some value given by a positional argument, named argument, or a default value (and that positional and named arguments do not overlap). +Note, that this only defines a valid call, but does not explicitly define the set of domains. +==== + +=== Overloaded Constructors + +Let `C` denote an operator record class and consider an expression `C(A₁, a₂, ..., aₖ, b₁=w₁, ..., bₚ=wₚ)`. + +. [[overloaded-constructor-unique]]If there exists a unique function _f_ in `C.'constructor'` such that (_A₁_, _a₂_, ..., _aₖ_, _b₁_=_w₁_, ..., _bₚ_=_wₚ_) is a valid match for the function _f_, then +`C(A₁, a₂, ..., aₖ, b₁=w₁, ..., bₚ=wₚ)` +is resolved to +`C.'constructor'.f(A₁, a₂, ..., aₖ, b₁=w₁, ..., bₚ=wₚ)`. + +. If there is no operator `C.'constructor'` the automatically generated record constructor is called. + +. Otherwise the expression is erroneous. + +Restrictions: + +* The operator `C.'constructor'` shall only contain functions that declare one output component, which shall be of the operator record class `C`. +* For an operator record class there shall not exist any potential call that lead to multiple matches in item 1 above. ++ +[NOTE] +==== +How to verify this is not specified. +==== + +* For a pair of operator record classes `C` and `D` and components `c` and `d` of these classes, respectively, at most one of `C.'constructor'(d)` and `D.'constructor'(c)` shall be legal. ++ +[NOTE] +==== +Hence, one of the two definitions must be removed. +==== + +[NOTE] +==== +By the last restriction the following problem for binary operators is avoided: + +Assume there are two operator record classes `C` and `D` that both have a constructor from `Real`. +If we want to extend `c + c` and `d + d` to support mixed operations, one variant would be to define `c + d` and `d + c`; but then `c + 2` becomes ambiguous (since it is not clear which instance should be converted to). +Without mixed operations expressions such as `c + d` are only ambiguous if both conversion from `C` to `D` and back from `D` to `C` are both available, and this possibility is not allowed by the restriction above. +==== + +Additionally there is an operator `'0'` defining the zero-value which can also be used to construct an element. +The operator `'0'` for an operator record `C` can contain only one function, having zero inputs and one output of type `C` (the called function is therefore unambiguous). +It should return the identity element of addition, and is used for generating flow-equations for `connect`-equations and zero elements for matrix multiplication. + +=== Overloaded String Conversions + +Consider an expression `String(A₁, a₂, ..., aₖ, b₁=w₁, ..., bₚ=wₚ)`, _k_ ≥ 1 where _A₁_ is an element of class `A`. + +. If `A` is a predefined type except `String` (i.e., `Boolean`, `Integer`, `Real` or an enumeration), or derived from such a type, then the corresponding built-in operation is performed. + +. [[binary-operator-record-unique-match]]If `A` is an operator record class and there exists a unique function _f_ in `A.'String'` such that +`A.'String'.f(A₁, a₂, ..., aₖ, b₁=w₁, ..., bₚ=wₚ)` +is a valid match for _f_, then +`String(A₁, a₂, ..., aₖ, b₁=w₁, ..., bₚ=wₚ)` +is evaluated to + +`A.'String'.f(A₁, a₂, ..., aₖ, b₁=w₁, ..., bₚ=wₚ)`. + +. Otherwise the expression is erroneous. + +Restrictions: + +* The operator `A.'String'` shall only contain functions that declare one output component, which shall be of the `String` type, and the first input argument shall be of the operator record class `A`. +* For an operator record class there shall not exist any call that lead to multiple matches in item 2 above. ++ +[NOTE] +==== +How to verify this is not specified. +==== + +=== Overloaded Binary Operations + +Let _X_ denote a binary operator and consider an expression `a X b` where `a` is an instance or array of instances of class `A` and `b` is an instance or array of instances of class `B`. + +. [[overloaded-binary-predefined]]If `A` and `B` are predefined types of such, then the corresponding built-in operation is performed. + +. [[overloaded-binary-unique]]Otherwise, if there exists _exactly one_ function _f_ in the union of `A.X` and `B.X` such that `f(a, b)` is a valid match for the function _f_, then `a X b` is evaluated using this function. +It is an error, if multiple functions match. +If `A` is not an operator record class, `A.X` is seen as the empty set, and similarly for `B`. ++ +[NOTE] +==== +Having a union of the operators ensures that if `A` and `B` are the same, each function only appears once. +==== ++ +Note that if the operations take array arguments, they will in this step only match if the number of dimensions match. + +. Otherwise, consider the set given by _f_ in `A.X` and an operator record class `C` (different from `B`) with a constructor, _g_, such that +`C.'constructor'.g(b)` +is a valid match, and +`f(a, C.'constructor'.g(b))` +is a valid match; and another set given by _f_ in `B.X` and an operator record class `D` (different from `A`) with a constructor, _h_, such that +`D.'constructor'.h(a)` +is a valid match and +`f(D.'constructor'.h(a), b)` +is a valid match. +If the sum of the sizes of these sets is one this gives the unique match. +If the sum of the sizes is larger than one it is an error. +Note that if the operations take array arguments, they will in this step only match if the number of dimensions match. ++ +[NOTE] +==== +Informally, this means: +If there is no direct match of `a X b`, then it is tried to find a direct match by automatic type casts of `a` or `b`, by converting either `a` or `b` to the needed type using an appropriate constructor function from one of the operator record classes used as arguments of the overloaded `op` functions. +Example using the `Complex`-definition below: +[source,modelica] +---- +Real a; +Complex b; +Complex c = a * b; // interpreted as: +// Complex.'*'.multiply(Complex.'constructor'.fromReal(a), b); +---- +==== + +. [[overloaded-binary-arrays]]Otherwise, if `a` or `b` is an array expression, then the expression is conceptually evaluated according to the rules of <> with the following exceptions concerning <>: +.. `vector * vector` is not automatically defined based on the scalar multiplication. ++ +[NOTE] +==== +The scalar product of <> does not generalize to the expected linear and conjugate linear scalar product of complex numbers. +It is possible to define a specific product function taking two array arguments handling this case. +==== +.. `vector * matrix` is not automatically defined based on the scalar multiplication. ++ +[NOTE] +==== +The corresponding definition of <> does not generalize to complex numbers in the expected way. +It is possible to define a specific product function taking two array arguments handling this case. +==== +.. If the inner dimension for `matrix * vector` or `matrix * matrix` is zero, this uses the overloaded `'0'` operator of the result array element type. +If the operator `'0'` is not defined for that class it is an error if the inner dimension is zero. + ++ +[NOTE] +==== +For array multiplication it is assumed that the scalar elements form a non-commutative ring that does not necessarily have a multiplicative identity. +==== + +. [[overloaded-binary-error]]Otherwise the expression is erroneous. + +For an element-wise operator, `a .op b`, items 1, 4 and 5 are used; e.g., the operator `.+` will always be defined in terms of `'+'`. + +Restrictions: + +* A function is allowed for a binary operator if and only if it has at least two inputs; at least one of which is of the operator record class, and the first two inputs shall not have default values, and all inputs after the first two must have default values. + +* For an operator record class there shall not exist any (potential) call that lead to multiple matches in item 2 above. + +=== Overloaded Unary Operations + +Let _X_ denote a unary operator and consider an expression `X a` where `a` is an instance or array of instances of class `A`. +Then `X a` is evaluated in the following way. + +. If `A` is a predefined type, then the corresponding built-in operation is performed. + +. [[unary-operator-record-unique-match]]If `A` is an operator record class and there exists a unique function _f_ in `A.X` such that `A.X.f(a)` is a valid match, then `X a` is evaluated to `A.X.f(a)`. +It is an error, if there are multiple valid matches. +Note that if the operations take array arguments, they will in this step only match if the number of dimensions match. + +. [[overloaded-unary-array]]Otherwise, if `a` is an array expression, then the expression is conceptually evaluated according to the rules of <>. + +. Otherwise the expression is erroneous. + +Restrictions: + +* A function is allowed for a unary operator if and only if it has least one input; and the first input is of the record type (or suitable arrays of such) and does not have a default value, and all inputs after the first one must have default values. + +* For an operator record class there shall not exist any (potential) call that lead to multiple matches in item 2 above. + +* A binary and/or unary operator-class may only contain functions that are allowed for this binary and/or unary operator-class; and in case of `'-'` it is the union of these sets, since it may define both a unary (negation) and binary (subtraction) operator. + +=== Example of Overloading for Complex Numbers + +[example] +==== +Example: The rules in the previous subsections are demonstrated at hand of a record class to work conveniently with complex numbers: + +[source,modelica] +---- +operator record Complex "Record defining a Complex number" + Real re "Real part of complex number"; + Real im "Imaginary part of complex number"; + encapsulated operator 'constructor' + import Complex; + function fromReal + input Real re; + input Real im = 0; + output Complex result(re = re, im = im); + algorithm + annotation(Inline = true); + end fromReal; + end 'constructor'; + + encapsulated operator function '+' // short hand notation, see specialized-classes + import Complex; + input Complex c1; + input Complex c2; + output Complex result "= c1 + c2"; + algorithm + result := Complex(c1.re + c2.re, c1.im + c2.im); + annotation(Inline = true); + end '+'; + + encapsulated operator '-' + import Complex; + function negate + input Complex c; + output Complex result "= - c"; + algorithm + result := Complex(-c.re, -c.im); + annotation(Inline = true); + end negate; + + function subtract + input Complex c1; + input Complex c2; + output Complex result "= c1 - c2"; + algorithm + result := Complex(c1.re - c2.re, c1.im - c2.im); + annotation(Inline = true); + end subtract; + end '-'; + + encapsulated operator function '*' + import Complex; + input Complex c1; + input Complex c2; + output Complex result "= c1 * c2"; + algorithm + result := + Complex(c1.re * c2.re - c1.im * c2.im, c1.re * c2.im + c1.im * c2.re); + annotation(Inline = true); + end '*'; + + encapsulated operator function '/' + import Complex; input Complex c1; + input Complex c2; + output Complex result "= c1 / c2"; + algorithm + result := + Complex((c1.re*c2.re + c1.im*c2.im) / (c2.re^2 + c2.im^2), + (-c1.re*c2.im + c1.im*c2.re) / (c2.re^2 + c2.im^2)); + annotation(Inline = true); + end '/'; + + encapsulated operator function '==' + import Complex; + input Complex c1; + input Complex c2; + output Boolean result "= c1 == c2"; + algorithm + result := c1.re == c2.re and c1.im == c2.im; + annotation(Inline = true); + end '=='; + + encapsulated operator function 'String' + import Complex; + input Complex c; + input String name = "j" + "Name of variable representing sqrt(-1) in the string"; + input Integer significantDigits = 6 + "Number of significant digits to be shown"; + output String s; + algorithm + s := String(c.re, significantDigits = significantDigits); + if c.im <> 0 then + s := if c.im > 0 then s + " + " else s + " - "; + s := s + String(abs(c.im), significantDigits = significantDigits) + name; + end if; + end 'String'; + + encapsulated function j + import Complex; + output Complex c; + algorithm + c := Complex(0, 1); + annotation(Inline = true); + end j; + + encapsulated operator function '0' + import Complex; + output Complex c; + algorithm + c := Complex(0, 0); + annotation(Inline = true); + end '0'; +end Complex; + +function eigenValues + input Real A [:,:]; + output Complex ev[size(A, 1)]; + protected + Integer nx = size(A, 1); + Real eval[nx, 2]; + Integer i; +algorithm + eval := Modelica.Math.Matrices.eigenValues(A); + for i in 1 : nx loop + ev[i] := Complex(eval[i, 1], eval[i, 2]); + end for; +end eigenValues; + +// Usage of Complex number above: + Complex j = Complex.j(); + Complex c1 = 2 + 3 * j; + Complex c2 = 3 + 4 * j; + Complex c3 = c1 + c2; + Complex c4[:] = eigenValues([1, 2; -3, 4]); +algorithm + Modelica.Utilities.Streams.print("c4 = " + String(c4)); + // results in output: + // c4 = {2.5 + 1.93649j, 2.5 - 1.93649j} +---- + +How overloaded operators can be symbolically processed. +Example: + +[source,modelica] +---- +Real a; +Complex b; +Complex c = a + b; +---- + +Due to inlining of functions, the equation for `c` is transformed to: + +[source,modelica] +---- +c = Complex.'+'.add(Complex.'constructor'.fromReal(a), b); + = Complex.'+'.add(Complex(re = a, im = 0), b) + = Complex(re = a + b.re, im = b.im); +---- + +or + +[source,modelica] +---- +c.re = a + b.re; +c.im = b.im; +---- + +These equations can be symbolically processed as other equations. + +Complex can be used in a connector: + +[source,modelica] +---- + operator record ComplexVoltage = Complex(re(unit = "V"), im(unit = "V")); + operator record ComplexCurrent = Complex(re(unit = "A"), im(unit = "A")); + + connector ComplexPin + ComplexVoltage v; + flow ComplexCurrent i; + end ComplexPin; + + ComplexPin p1, p2, p3; +equation + connect(p1, p2); + connect(p1, p3); +---- + +The two `connect`-equations result in the following connection equations: + +[source,modelica] +---- +p1.v = p2.v; +p1.v = p3.v; +p1.i + p2.i + p3.i = Complex.'0'(); +// Complex.'+'(p1.i, Complex.'+'(p2.i, p3.i)) = Complex.'0'(); +---- + +The restrictions on extends are intended to avoid combining two variants inheriting from the same operator record, but with possibly different operations; thus `ComplexVoltage` and `ComplexCurrent` still use the operations from `Complex`. +The restriction that it is not legal to extend from any of its enclosing scopes implies that: + +[source,modelica] +---- +package A + extends Icon; // Ok + operator record B ... end B; +end A; + +package A2 + extends A(...); // Not legal +end A2; + +package A3 = A(...); // Not legal +---- +==== \ No newline at end of file diff --git a/docs/15__stream.adoc b/docs/15__stream.adoc new file mode 100644 index 000000000..d437d5bae --- /dev/null +++ b/docs/15__stream.adoc @@ -0,0 +1,335 @@ +== Stream Connectors +:id: stream-connectors + +The two basic variable types in a connector -- _potential_ (or _across_) variable and _flow_ (or _through_) variable -- are not sufficient to describe in a numerically sound way the bi-directional flow of matter with convective transport of specific quantities, such as specific enthalpy and chemical composition. +The values of these specific quantities are determined from the upstream side of the flow, i.e., they depend on the flow direction. +When using across and through variables, the corresponding models would include nonlinear systems of equations with `Boolean` unknowns for the flow directions and singularities around zero flow. +Such equation systems cannot be solved reliably in general. +The model formulations can be simplified when formulating two different balance equations for the two possible flow directions. +This is not possible with across and through variables though. + +This fundamental problem is addressed in Modelica by introducing a third type of connector variable, called a _stream variable_, declared with the prefix `stream`. +A stream variable describes a quantity that is carried by a flow variable, i.e., a purely convective transport phenomenon. +The value of the stream variable is the specific property inside the component close to the boundary, assuming that matter flows out of the component into the connection point. +In other words, it is the value the carried quantity would have if the fluid was flowing out of the connector, irrespective of the actual flow direction. + +The rationale of the definition and typical use cases are described in <>. + +=== Definition of Stream Connectors + +If at least one variable in a connector has the `stream` prefix, the connector is called a _stream connector_ and the corresponding variable is called a _stream variable_. +The following definitions hold: + +* The `stream` prefix can only be used in a connector declaration. + +* A stream connector must have exactly one variable with the `flow` prefix. That variable shall be a scalar that is a subtype of `Real`. ++ +[NOTE] +The idea is that all stream variables of a connector are associated with this flow variable. + +* For every outside connector (see <>), one equation is generated for every variable with the `stream` prefix (to describe the propagation of the stream variable along a model hierarchy). For the exact definition, see the end of <>. + +* For inside connectors (see <>), variables with the `stream` prefix do not lead to connection equations. + +* Connection equations with stream variables are generated in a model when using `inStream` or `actualStream`, see <> and <>. + +The variable with `flow` prefix must exist at the same level of the connector as the variable with `stream` prefix; i.e., they can each be directly declared in the connector or be inherited, while a stream variable in a sub-connector would result in a separate stream sub-connector. + +[example] +==== +Example: + +[source,modelica] +---- +connector FluidPort + replaceable package Medium = + Modelica.Media.Interfaces.PartialMedium; + Medium.AbsolutePressure p "Pressure in connection point"; + flow Medium.MassFlowRate m_flow "> 0, if flow into component"; + stream Medium.SpecificEnthalpy h_outflow "h close to port if m_flow < 0"; + stream Medium.MassFraction X_outflow[Medium.nX] "X close to port if m_flow < 0"; +end FluidPort; +---- + +`FluidPort` is a stream connector, because some connector variables have the `stream` prefix. +The Medium definition and the stream variables are associated with the only flow variable (`m_flow`) that defines a fluid stream. +The Medium and the stream variables are transported with this flow variable. +The stream variables `h_outflow` and `X_outflow` are the stream properties inside the component close to the boundary, when fluid flows out of the component into the connection point. +The stream properties for the other flow direction can be inquired with the built-in `inStream`. +The value of the stream variable corresponding to the actual flow direction can be inquired through the built-in `actualStream`, see <>. +==== + +=== inStream and Connection Equations +:id: stream-operator-instream-and-connection-equations + +In combination with the stream variables of a connector, `inStream` is designed to describe in a numerically reliable way the bi-directional transport of specific quantities carried by a flow of matter. + +`inStream(v)` is only allowed on stream variables `v` and is informally the value the stream variable has, assuming that the flow is from the connection point into the component. +This value is computed from the _stream connection equations_ of the flow variables and of the stream variables. + +For the following definition it is assumed that `N` inside connectors `m~j~.c(j = 1, 2, ..., N)` and `M` outside connectors `c~k~(k = 1, 2, ..., M)` belonging to the same connection set (see definition in <>) are connected together and a stream variable `h_outflow` is associated with a flow variable `m_flow` in connector `c`. + +[source,modelica] +---- +connector FluidPort + ... + flow Real m_flow "Flow of matter; m_flow > 0 if flow into component"; + stream Real h_outflow "Specific variable in component if m_flow < 0" +end FluidPort + +model FluidSystem + ... + FluidComponent m_1, m_2, ..., m_N; + FluidPort c_1, c_2, ..., c_M; +equation + connect(m_1.c, m_2.c); + connect(m_1.c, m_3.c); + ... + connect(m_1.c, m_N.c); + connect(m_1.c, c_1); + connect(m_1.c, c_2); + ... + connect(m_1.c, c_M); + ... +end FluidSystem; +---- + +.Examplary `FluidSystem` with 𝑁 = 3 and 𝑀 = 2. +image::media/fluidsystem.svg[] + +[NOTE] +The connection set represents an infinitesimally small control volume, for which the stream connection equations are equivalent to the conservation equations for mass and energy. + +With these prerequisites, the semantics of the expression `inStream(m_i.c.h_outflow)` is given implicitly by defining an additional variable `h_mix_in_i`, and by adding to the model the conservation equations for mass and energy corresponding to the infinitesimally small volume spanning the connection set. +The connection equation for the flow variables has already been added to the system according to the connection semantics of flow variables defined in <>. + +[source,modelica] +---- +// Standard connection equation for flow variables +0 = sum(m_j.c.m_flow for j in 1:N) + sum(-c_k.m_flow for k in 1:M); +---- + +Whenever `inStream` is applied to a stream variable of an inside connector, the balance equation of the transported property must be added under the assumption of flow going into the connector + +[source,modelica] +---- +// Implicit definition of inStream applied to inside connector i +0 = + sum(m_j.c.m_flow * + (if m_j.c.m_flow > 0 or j==i then h_mix_in_i else m_j.c.h_outflow) + for j in 1:N) + + sum(-c_k.m_flow * + (if -c_k.m_flow > 0 then h_mix_in_i else inStream(c_k.h_outflow) + for k in 1:M); +inStream(m_i.c.h_outflow) = h_mix_in_i; +---- + +Note that the result of `inStream(m_i.c.h_outflow)` is different for each port i, because the assumption of flow entering the port is different for each of them. + +Additional equations need to be generated for the stream variables of outside connectors. + +[source,modelica] +---- +// Additional connection equations for outside connectors +for q in 1:M loop + 0 = + sum(m_j.c.m_flow * + (if m_j.c.m_flow > 0 then h_mix_out_q else m_j.c.h_outflow) + for j in 1:N) + + sum(-c_k.m_flow * + (if -c_k.m_flow > 0 or k==q then h_mix_out_q else inStream(c_k.h_outflow)) + for k in 1:M); + c_q.h_outflow = h_mix_out_q; +end for; +---- + +Neglecting zero flow conditions, the solution of the above-defined stream connection equations for `inStream` values of inside connectors and outflow stream variables of outside connectors is (for a derivation, see <>): + +[source,modelica] +---- +inStream(m_i.c.h_outflow) := + (sum(max(-m_j.c.m_flow,0)*m_j.c.h_outflow for j in cat(1, 1:i-1, i+1:N) + + sum(max( c_k.m_flow,0)*inStream(c_k.h_outflow) for k in 1:M)) + / + (sum(max(-m_j.c.m_flow,0) for j in cat(1, 1:i-1, i+1:N) + + sum(max( c_k.m_flow ,0) for k in 1:M)); + +// Additional equations to be generated for outside connectors q +for q in 1:M loop + c_q.h_outflow := + (sum(max(-m_j.c.m_flow,0)*m_j.c.h_outflow for j in 1:N) + + sum(max( c_k.m_flow,0)*inStream(c_k.h_outflow) for k in cat(1, 1:q-1, q+1:M)) + / + (sum(max(-m_j.c.m_flow,0) for j in 1:N) + + sum(max( c_k.m_flow ,0) for k in cat(1, 1:q-1, q+1:M))); +end for; +---- + +[NOTE] +Note, that `inStream(c_k.h_outflow)` is computed from the connection set that is present one hierarchical level above. +At this higher level `c_k.h_outflow` is no longer an outside connector, but an inside connector and then the formula from above for inside connectors can be used to compute it. + +If the argument of `inStream` is an array, the implicit equation system holds elementwise, i.e., `inStream` is vectorizable. + +The stream connection equations have singularities and/or multiple solutions if one or more of the flow variables become zero. +When all the flows are zero, a singularity is always present, so it is necessary to approximate the solution in an open neighbourhood of that point. + +[NOTE] +For example, assume that `m_j.c.m_flow = c_k.m_flow = 0`, then all equations above are identically fulfilled and `inStream` can have any value. + +However, specific optimizations may be applied to avoid the regularization if the flow through one port is zero or non-negative, see <>. +It is required that `inStream` is appropriately approximated when regularization is needed and the approximation must fulfill the following requirements: + +. `inStream(m_i.c.h_outflow)` and `inStream(c_k.h_outflow)` must be unique with respect to all values of the flow and stream variables in the connection set, and must have a continuous dependency on them. +. Every solution of the implicit equation system above must fulfill the equation system identically (up to the usual numerical accuracy), provided the absolute value of every flow variable in the connection set is greater than a small value (`abs(m_i.c.m_flow) > eps` for `1 ≤ i ≤ N` and `abs(c_i.m_flow) > eps` for `1 ≤ i ≤ M`). + +[NOTE] +-- +Based on the above requirements, the following implementation is recommended: + +* N = 1, M = 0: +[source,modelica] +---- +inStream(m_1.c.h_outflow) = m_1.c.h_outflow; +---- + +* N = 2, M = 0: +[source,modelica] +---- +inStream(m_1.c.h_outflow) = m_2.c.h_outflow; +inStream(m_2.c.h_outflow) = m_1.c.h_outflow; +---- + +* N = 1, M = 1: +[source,modelica] +---- +inStream(m_1.c.h_outflow) = inStream(c_1.h_outflow); +// Additional equation to be generated +c_1.h_outflow = m_1.c.h_outflow; +---- + +* N = 0, M = 2: +[source,modelica] +---- +// Additional equation to be generated +c_1.h_outflow = inStream(c_2.h_outflow); +c_2.h_outflow = inStream(c_1.h_outflow); +---- + +* All other cases: +[source,modelica] +---- +if m_j.c.m_flow.min >= 0 for all j = 1:N with j <> i and + c_k.m_flow.max <= 0 for all k = 1:M +then + inStream(m_i.c.h_outflow) = m_i.c.h_outflow; +else + si = sum (max(-m_j.c.m_flow,0) for j in cat(1,1:i-1, i+1:N) + + sum(max( c_k.m_flow ,0) for k in 1:M); + inStream(m_i.c.h_outflow) = + (sum(positiveMax(-m_j.c.m_flow,si)*m_j.c.h_outflow) + + sum(positiveMax(c_k.m_flow,si)*inStream(c_k.h_outflow)))/ + (sum(positiveMax(-m_j.c.m_flow,si)) + + sum(positiveMax(c_k.m_flow,si))) + for j in 1:N and i <> j and m_j.c.m_flow.min < 0, + for k in 1:M and c_k.m_flow.max > 0 +// Additional equations to be generated +for q in 1:M loop + if m_j.c.m_flow.min >= 0 for all j = 1:N and + c_k.m_flow.max <= 0 for all k = 1:M and k <> q + then + c_q.h_outflow = 0; + else + s_q = (sum(max(-m_j.c.m_flow,0) for j in 1:N) + + sum(max( c_k.m_flow ,0) for k in cat(1,1:q-1, q+1:M))); + c_q.h_outflow = (sum(positiveMax(-m_j.c.m_flow,s_q)*m_j.c.h_outflow) + + sum(positiveMax(c_k.m_flow,s_q)* inStream(c_k.h_outflow)))/ + (sum(positiveMax(-m_j.c.m_flow,s_q)) + + sum(positiveMax(c_k.m_flow,s_q))) + for j in 1:N and m_j.c.m_flow.min < 0, + for k in 1:M and k <> q and c_k.m_flow.max > 0 +end for; +---- + +The operator `positiveMax(-m_j.c.m_flow, si)` should be such that: + +* `positiveMax(-m_j.c.m_flow, si) = -m_j.c.m_flow` if `-m_j.c.m_flow > eps1_j ≥ 0`, where `eps1_j` are small flows, compared to typical problem-specific values, + +* all denominators should be greater than `eps2 > 0`, where `eps2` is also a small flow, compared to typical problem-specific values. + +Trivial implementation of `positiveMax` guarantees continuity of `inStream`: + +[source,modelica] +---- +positiveMax(-m_j.c.m_flow, si) = max(-m_j.c.m_flow, eps1); // so si is not needed +---- +More sophisticated implementation, with smooth approximation, applied only when all flows are small: +[source,modelica] +---- +// Define a "small number" eps (nominal(v) is the nominal value of v) +eps := relativeTolerance*min(nominal(m_j.c.m_flow)); + +// Define a smooth curve, such that alpha(si>=eps)=1 and alpha(si<0)=0 +alpha := smooth(1, if si > eps then 1 + else if si > 0 then (si/eps)^2*(3-2* si/eps) + else 0); + +// Define function positiveMax(v,si) as a linear combination of max (v,0) +// and of eps along alpha +positiveMax(-m_j.c.m_flow,si) := alpha*max(-m_j.c.m_flow,0) + (1-alpha)*eps; +---- + +The derivation of this implementation is discussed in <>. +Note that in the cases N = 1, M = 0 (unconnected port, physically corresponding to a plugged-up flange), and N = 2, M = 0 (one-to-one connection), the result of `inStream` is trivial and no non-linear equations are left in the model, despite the fact that the original definition equations are nonlinear. + +The following properties hold for this implementation: + +* `inStream` is continuous (and differentiable), provided that `m_j.c.h_outflow`, `m_j.c.m_flow`, `c_k.h_outflow`, and `c_k.m_flow` are continuous and differentiable. + +* A division by zero can no longer occur (since `sum(positiveMax(-m_j.c.m_flow, si)) ≥ eps2 > 0`), so the result is always well-defined. + +* The balance equations are exactly fulfilled if the denominator is not close to zero (since the exact formula is used, if `sum(positiveMax(-m_j.c.m_flow, si)) > eps`). + +* If all flows are zero, `inStream(m_i.c.h_outflow) = sum(m_j.c.h_outflow for j ≠ i and m_j.c.m_flow.min < 0) / N_p`, i.e., it is the mean value of all the N_p variables `m_j.c.h_outflow`, such that j ≠ i and `m_j.c.m_flow.min < 0`. + This is a meaningful approximation, considering the physical diffusion effects that are relevant at small flow rates in a small connection volume (thermal conduction for enthalpy, mass diffusion for mass fractions). + +The value of `relativeTolerance` should be larger than the relative tolerance of the nonlinear solver used to solve the implicit algebraic equations. + +As a final remark, further symbolic simplifications could be carried out by taking into account equations that affect the flows in the connection set (i.e., equivalent to `m_j.c.m_flow = 0`, which then implies `m_j.c.m_flow.min ≥ 0`). +This is interesting, e.g., in the case of a valve when the stem position is set identically to closed by its controller. +-- + +=== actualStream +:id: stream-operator-actualstream + +`actualStream` is provided for convenience, in order to return the actual value of the stream variable, depending on the actual flow direction. +The only argument of this built-in operator needs to be a reference to a stream variable. +The operator is vectorizable, in the case of vector arguments. +For the following definition it is assumed that an (inside or outside) connector `c` contains a stream variable `h_outflow` which is associated with a flow variable `m_flow` in the same connector `c`: + +[source,modelica] +---- +actualStream(c.h_outflow) = + if c.m_flow > 0 then inStream(c.h_outflow) else c.h_outflow; +---- + +[NOTE] +-- +`actualStream` is typically used in two contexts: + +[source,modelica] +---- +der(U) = c.m_flow * actualStream(c.h_outflow); // (1) energy balance equation +h_c = actualStream(c.h); // (2) monitoring the enthalpy at port c +---- + +In the case of equation (1), although `actualStream` is discontinuous, the product with the flow variable is not, because `actualStream` is discontinuous when the flow is zero by construction. +Therefore, a tool might infer that the expression is `smooth(0, ...)` automatically, and decide whether or not to generate an event. +If a user wants to avoid events entirely, he/she may enclose the right-hand side of (1) with `noEvent`. + +Equations like (2) might be used for monitoring purposes (e.g., plots), in order to inspect what the actual enthalpy of the fluid flowing through a port is. +In this case, the user will probably want to see the change due to flow reversal at the exact instant, so an event should be generated. +If the user doesn't bother, then he/she should enclose the right-hand side of (2) with `noEvent`. +Since the output of `actualStream` will be discontinuous, it should not be used by itself to model physical behaviour (e.g., to compute densities used in momentum balances) -- `inStream` should be used for this purpose. +`actualStream` should be used to model physical behaviour only when multiplied by the corresponding flow variable (like in the above energy balance equation), because this removes the discontinuity. +-- \ No newline at end of file diff --git a/docs/16__synchronous.adoc b/docs/16__synchronous.adoc new file mode 100644 index 000000000..c0004dcdb --- /dev/null +++ b/docs/16__synchronous.adoc @@ -0,0 +1,1725 @@ +== Synchronous Language Elements +:id: synchronous-language-elements + +This chapter defines synchronous behavior suited for implementation of control systems. +The synchronous behavior relies on an additional kind of discrete-time variables and equations, as well as an additional kind of `when`-clause. +The benefits of synchronous behavior is that it allows a model to define large sampled data systems in a safe way, so that the translator can provide good diagnostics in case of a modeling error. + +The following small example shows the most important elements: + +.A continuous plant and a sampled data controller connected together with sample and (zero-order) hold elements. +image::media/plantmodel.svg[] + +* A periodic clock is defined with `Clock(3)`. +The argument of `Clock` defines the sampling interval (for details see <>). + +* Clocked variables (such as `yd`, `xd`, `ud`) are associated uniquely with a clock and can only be directly accessed when the associated clock is active. +Since all variables in a clocked equation must belong to the same clock, clocking errors can be detected at compile time. +If variables from different clocks shall be used in an equation, explicit cast operators must be used, such as `sample` to convert from continuous-time to clocked discrete-time or `hold` to convert from clocked discrete-time to continuous-time. + +* A continuous-time variable is sampled at a clock tick with `sample`. +The operator returns the value of the continuous-time variable when the clock is active. + +* When no argument is defined for `Clock`, the clock is deduced by clock inference. + +* For a `when`-clause with an associated clock, all equations inside the `when`-clause are clocked with the given clock. All equations on an associated clock are treated together and in the same way regardless of whether they are inside a `when`-clause or not. This means that automatic sampling and hold of variables inside the `when`-clause does not apply (explicit sampling and hold is required) and that general equations can be used in such `when`-clauses (this is not allowed for `when`-clauses with `Boolean` conditions, that require a variable reference on the left-hand side of an equation). + +* The `when`-clause in the controller could also be removed and the controller could just be defined by the equations: ++ +[source,modelica] +---- +/* Discrete controller */ +E * xd = A * previous(xd) + B * yd; + ud = C * previous(xd) + D * yd; +---- + +* `previous(xd)` returns the value of `xd` at the previous clock tick. At the first sample instant, the start value of `xd` is returned. + +* A discrete-time signal (such as `ud`) is converted to a continuous-time signal with `hold`. + +* If a variable belongs to a particular clock, then all other equations where this variable is used, with the exception of as argument to certain special operators, belong also to this clock, as well as all variables that are used in these equations. +This property is used for clock inference and allows defining an associated clock only at a few places (above only in the sampler, whereas in the discrete controller and the hold the sampling period is inferred). + +* The approach in this chapter is based on the clock calculus and inference system proposed by <> and implemented in Lucid Synchrone version 2 and 3 (<>). +However, the Modelica approach also uses multi-rate periodic clocks based on rational arithmetic introduced by <>, as an extension of the Lucid Synchrone semantics. +These approaches belong to the class of synchronous languages (<>). + +=== Rationale for Clocked Semantics + +[NOTE] +==== +Periodically sampled control systems could also be defined with standard `when`-clauses, see <>, and the `sample` operator, see <>. +For example: + +[source,modelica] +---- +when sample(0, 3) then + xd = A * pre(xd) + B * y; + u = C * pre(xd) + D * y; +end when; +---- + +Equations in a `when`-clause with a `Boolean` condition have the property that (a) variables on the left hand side of the equal sign are assigned a value when the when-condition becomes true and otherwise hold their value, (b) variables not assigned in the `when`-clause are directly accessed (= automatic `sample` semantics), and (c) the variables assigned in the `when`-clause can be directly accessed outside of the `when`-clause (= automatic `hold` semantics). + +Using standard `when`-clauses works well for individual simple sampled blocks, but the synchronous approach using clocks and clocked equations provide the following benefits (especially for large sampled systems): + +. Possibility to detect inconsistent sampling rate, since clock partitioning (see <>), replaces the automatic sample and hold semantics. + Examples: + +.. If `when`-clauses in different blocks should belong to the same controller part, but by accident different when-conditions are given, then this is accepted (no error is detected). + +.. If a sampled data library such as the Modelica_LinearSystems2.Contoller library is used, at every block the sampling of the block has to be defined as integer multiple of a base sampling rate. + If several blocks should belong to the same controller part, and different integer multiples are given, then the translator has to accept this (no error is detected). + ++ +Note: Clocked systems can mix different sampling rates in well-defined ways when needed. + +. Fewer initial conditions are needed, as only a subset of clocked variables need initial conditions -- the clocked state variables (see <>). + For a standard `when`-clause all variables assigned in a `when`-clause must have an initial value because they might be used, before they are assigned a value the first time. + As a result, all these variables are ``discrete-time states'' although in reality only a subset of them need an initial value. + +. More general equations can be used, compared to standard `when`-clauses that require a restricted form of equations where the left hand side has to be a variable, in order to identify the variables that are assigned in the `when`-clause. + This restriction can be circumvented for standard `when`-clauses, but is absent for clocked equations and make it more convenient to define nonlinear control algorithms. + +. Clocked equations allow clock inference, meaning that the sampling need only be given once for a sub-system. + For a standard `when`-clause the condition (sampling) must be explicitly propagated to all blocks, which is tedious and error prone for large systems. + +. Possible to use general continuous-time models in synchronous models (e.g., some advanced controllers use an inverse model of a plant in the feedforward path of the controller, see <>). + This powerful feature of Modelica to use a nonlinear plant model in a controller would require to export the continuous-time model with an embedded integration method and then import it in an environment where the rest of the controller is defined. + With clocked equations, clocked controllers with continuous-time models can be directly defined in Modelica. + +. Clocked equations are straightforward to optimize because they are evaluated exactly once at each event instant. + In contrast a standard `when`-clause with `sample` conceptually requires several evaluations of the model (in some cases tools can optimize this to avoid unneeded evaluations). + The problem for the standard `when`-clause is that after `v` is changed, `pre(v)` shall be updated and the model re-evaluated, since the equations could depend on `pre(v)`. + For clocked equations this iteration can be omitted since `previous(v)` can only occur in the clocked equations that are only run the first event iterations. + +. Clocked subsystems using arithmetic blocks are straightforward to optimize. + When a standard math-block (e.g., addition) is part of a clocked sub-system it is automatically clocked and only evaluated when the clocked equations trigger. + For standard `when`-clauses one either needs a separate sampled math-block for each operation, or it will conceptually be evaluated all the time. + However, tools may perform a similar optimization for standard `when`-clauses and it is only relevant in large sampled systems. +==== + +=== Definitions + +In this section various terms are defined. + +==== Clocks and Clocked Variables + +In <> the term _discrete-time_ Modelica expression and in <> the term _continuous-time_ Modelica expression is defined. +In this chapter, two additional kinds of discrete-time expressions/variables are defined that are associated to clocks and are therefore called _clocked discrete-time_ expressions. +The different kinds of discrete-time variables in Modelica are defined below. + +Definition Piecewise-constant variable:: +(See <>.) +Variables _m(t)_ of base type `Real`, `Integer`, `Boolean`, enumeration, and `String` that are _constant_ inside each interval _tᵢ_ ≤ _t_ < _tᵢ₊₁_ (i.e., piecewise constant continuous-time variables). +In other words, _m(t)_ changes value only at events: _m(t)_ = _m(tᵢ)_, for _tᵢ_ ≤ _t_ < _tᵢ₊₁_. +Such variables depend continuously on time and they are discrete-time variables. +See <>. ++ +[[fig-piecewise-constant-variable]] +.A piecewise-constant variable +image::media/piecewise-constant.svg[width=30%] + +Definition Clock variable:: +Clock variables _c(tᵢ)_ are of base type `Clock`. +A clock is either defined by a constructor (such as `Clock(3)`) that defines when the clock ticks (is active) at a particular time instant, or it is defined with clock operators relatively to other clocks, see <>. +See <>. ++ +[example] +==== +Example: Clock variables: + +[source,modelica] +---- +Clock c1 = Clock(...); +Clock c2 = c1; +Clock c3 = subSample(c2, 4); +---- +==== ++ +[[fig-clock-variable]] +.A clock variable. The value of a clock variable is not defined -- the plot marks only indicate _when_ the clock is active. +image::media/clock.svg[width=30%] + +[[def-clocked-variable,Definition Clocked variable]] +Definition Clocked variable:: +The elements of clocked variables _r(tᵢ)_ are of base type `Real`, `Integer`, `Boolean`, enumeration, `String` that are associated uniquely with a clock _c(tᵢ)_. +A clocked variable can only be directly accessed at the event instant where the associated clock is active. +A constant and a parameter can always be used at a place where a clocked variable is required. ++ +[NOTE] +Note that clock variables are not included in this list. +This implies that clock variables cannot be used where clocked variables are required. ++ +At time instants where the associated clock is not active, the value of a clocked variable can be inquired by using an explicit cast operator, see below. +In such a case `hold` semantics is used, in other words the value of the clocked variable from the last event instant is used. +See <>. ++ +[[fig-clocked-variable]] +.A clocked variable. The `hold` extrapolation of the value at the last event instant is illustrated with dashed green lines. +image::media/clocked.svg[width=30%] + +==== Base- and Sub-Partitions + +There are two kinds of _clock partitions_: + +Definition Base-partition:: +A base-partition identifies a set of equations and a set of variables which must be executed together in one task. +Different base-partitions can be associated to separate tasks for asynchronous execution. + +Definition Sub-partition:: +A sub-partition identifies a subset of equations and a subset of variables of a base-partition which are partially synchronized with other sub-partitions of the same base-partition, i.e., synchronized when the ticks of the respective clocks are simultaneous. ++ +The terminology for the partitions is as follows: ++ +* _Clocked base-partitions_. ++ +** _Discrete-time sub-partitions_. ++ +** _Discretized sub-partitions_. ++ +* _Unclocked base-partition_. ++ +[NOTE] +Note that the term _clock partition_ refers to these partitions in general, whereas _clocked base-partition_ is a specific kind of partition. +Previously the discrete-time sub-partitions were called _clocked discrete-time_ (_sub-clock_ partition). +Further, discretized sub-partitions were called _discretized continuous-time_ (_sub-clock_ partition). +When emphasizing that the partitions are clock partitions, sub-partitions can still be referred to as _sub-clock partitions_; and similarly for base-partition. + +==== Argument Restrictions (Component Expression) + +The built-in operators (with function syntax) defined in the following sections have partially restrictions on their input arguments that are not present for Modelica functions. +To define the restrictions, the following term is used. + +[[def-component-expression,Definition Component expression]] +Definition Component expression:: +A component expression is a `component-reference` which is a valid expression, i.e., not referring to models or blocks with equations. +In detail, it is an instance of a (a) base type, (b) derived type, (c) record, (d) an array of such an instance (a-c), (e) one or more elements of such an array (d) defined by index expressions which are evaluable (see below), or (f) an element of records. ++ +[NOTE] +The essential features are that one or several values are associated with the instance, that start values can be defined on these values, and that no equations are associated with the instance. +A component expression can be constant or can vary with time. + +In the following sections, when defining an operator with function calling syntax, there are some common restrictions being used for the input arguments (operands). +For example, an input argument to the operator may be required to be a component expression (<>) or evaluable expression (<>). +To emphasize that there are no such restrictions, an input argument may be said to be just an _expression_. + +[NOTE] +-- +The reason for restricting an input argument to be a component expression is that the start value of the input argument is returned before the first tick of the clock of the input argument and this is not possible for a general expression. + +The reason for restricting an input argument to be an evaluable expression is to ensure that clock analysis can be performed during translation. +In cases when special handling of parameter expressions is specified, it is an indication that the values are not needed during translation. +-- + +[example] +==== +Example: The input argument to `previous` is restricted to be a component expression. + +[source,modelica] +---- +Real u1; +Real u2[4]; +Complex c; +Resistor R; +... +y1 = previous(u1); // fine +y2 = previous(u2); // fine +y3 = previous(u2[2]); // fine +y4 = previous(c.im); // fine +y5 = previous(2 * u); // error (general expression, not component expression) +y6 = previous(R); // error (component, not component expression) +---- +==== + +[example] +==== +Example: The named argument `factor` of `subSample` is restricted to be an evaluable expression. + +[source,modelica] +---- +Real u; +parameter Real p=3; +... +y1 = subSample(u, factor = 3); // fine (literal) +y2 = subSample(u, factor = 2 * p - 3); // fine (evaluable expression) +y3 = subSample(u, factor = 3 * u); // error (general expression) +---- +==== + +None of the operators defined in this chapter vectorize, but some can operate directly on array variables (including clocked array variables, but not clock array variables). +They are not callable in functions. + +=== Clock Constructors + +The overloaded constructors listed below are available to generate clocks, and it is possible to call them with the specified named arguments, or with positional arguments (according to the order shown in the details after the table). + +[cols="1,1,1"] +|=== +|Expression |Description |Details + +|`Clock()` +|Inferred clock +|<> + +|`Clock(intervalCounter, resolution)` +|Rational interval clock +|<> + +|`Clock(interval)` +|Real interval clock +|<> + +|`Clock(condition, startInterval)` +|Event clock +|<> + +|`Clock(c, solverMethod)` +|Solver clock +|<> +|=== + +[[operator:clock-inferred]] +Operator Clock:: ++ +[source,modelica] +---- +Clock() +---- ++ +_Inferred clock_. +The operator returns a clock that is inferred. ++ +[example] +==== +[source,modelica] +---- +when Clock() then // equations are on the same clock + x = A * previous(x) + B * u; + Modelica.Utilities.Streams.print + ("clock ticks at = " + String(sample(time))); +end when; +---- + +Note, in most cases, the operator is not needed and equations could be written without a `when`-clause (but not in the example above, since the `print` statement is otherwise not associated to a clock). +This style is useful if a modeler would clearly like to mark the equations that must belong to one clock (although a tool could figure this out as well, if the `when`-clause is not present). +==== + +[[operator:clock-rational]] +Operator Clock:: ++ +[source,modelica] +---- +Clock(intervalCounter=intervalCounter, resolution=resolution) +---- ++ +_Rational interval clock_. +The first input argument, _intervalCounter_, is a clocked component expression (<>) or an evaluable expression of type `Integer` with `min = 0`. +The optional second argument _resolution_ (defaults to 1) is an evaluable expression of type `Integer` with `min = 1` and `unit = "Hz"`. +If _intervalCounter_ is an evaluable expression with value zero, the period of the clock is derived by clock inference, see <>. ++ +If _intervalCounter_ is an evaluable expression greater than zero, the clock defines a periodic clock. +If _intervalCounter_ is a clocked component expression it must be greater than zero. +The result is of base type `Clock` that ticks when `time` becomes _t~start~_, _t~start~_ + _interval~1~_, _t~start~_ + _interval~1~_ + _interval~2~_, ... +The clock starts at the start of the simulation _t~start~_ or when the controller is switched on. +At the start of the simulation, `previous(intervalCounter)` = `intervalCounter.start` and the clocks ticks the first time. +At the first clock tick _intervalCounter_ must be computed and the second clock tick is then triggered at _interval~1~_ = _intervalCounter_/_resolution_. +At the second clock tick at time _t~start~_ + _interval~1~_, a new value for _intervalCounter_ must be computed and the next clock tick is scheduled at _interval~2~_ = _intervalCounter_/_resolution_, and so on. ++ +[NOTE] +The given interval and time shift can be modified by using the `subSample`, `superSample`, `shiftSample` and `backSample` operators on the returned clock, see <>. ++ +[example] +==== +Example: + +[source,modelica] +---- + // first clock tick: previous(nextInterval) = 2 + Integer nextInterval(start = 2); + Real y1(start = 0); + Real y2(start = 0); +equation + when Clock(2, 1000) then + // periodic clock that ticks at 0, 0.002, 0.004, ... + y1 = previous(y1) + 1; + end when; + + when Clock(nextInterval, 1000) then + // interval clock that ticks at 0, 0.003, 0.007, 0.012, ... + nextInterval = previous(nextInterval) + 1; + y2 = previous(y2) + 1; + end when; +---- +==== ++ +Note that operator `interval(c)` of `Clock c = Clock(nextInterval, resolution)` returns: + +`previous(intervalCounter) / resolution` (in seconds) + +[[operator:clock-interval]] +Operator Clock:: ++ +[source,modelica] +---- +Clock(interval=interval) +---- ++ +_Real interval clock_. +The input argument, _interval_, is a clocked component expression (<>) or a parameter expression. +The _interval_ must be strictly positive (_interval_ > 0) of type `Real` with `unit = "s"`. +The result is of base type `Clock` that ticks when `time` becomes _t~start~_, _t~start~_ + _interval~1~_, _t~start~_ + _interval~1~_ + _interval~2~_, ... +The clock starts at the start of the simulation _t~start~_ or when the controller is switched on. +Here the next clock tick is scheduled at _interval~1~_ = `previous(interval)` = `interval.start`. +At the second clock tick at time _t~start~_ + _interval~1~_, the next clock tick is scheduled at _interval~2~_ = `previous(interval)`, and so on. +If _interval_ is a parameter expression, the clock defines a periodic clock. ++ +[NOTE] +Note, the clock is defined with `previous(interval)`. +Therefore, for sorting the input argument is treated as known. +The given interval and time shift can be modified by using the `subSample`, `superSample`, `shiftSample` and `backSample` operators on the returned clock, see <>. +There are restrictions where this operator can be used, see `Clock` expressions below. +Note that _interval_ does not have to an evaluable expression, since different real interval clocks are never compared. + +[[operator:clock-event]] +Operator Clock:: ++ +[source,modelica] +---- +Clock(condition=condition, startInterval=startInterval) +---- ++ +_Event clock_. +The first input argument, _condition_, is a continuous-time expression of type `Boolean`. +The optional _startInterval_ argument (defaults to 0) is the value returned by `interval()` at the first tick of the clock, see <>. +The result is of base type `Clock` that ticks when `edge(pre(condition))` becomes `true`. ++ +[NOTE] +This clock is used to trigger a clocked base-partition due to a state event (that is a zero-crossing of a `Real` variable) in an unclocked base-partition, or due to a hardware interrupt that is modeled as `Boolean` in the simulation model. +The additional `pre` delays the event clock one event iteration, but does not introduce any time-delay. ++ +[example] +==== +Example: + +[source,modelica] +---- +Clock c = Clock(angle > 0, 0.1); // before first tick of c: + // interval(c) = 0.1 +---- +==== ++ +[example] +==== +Example: Demonstrating the subtle effect of the additional `pre`: + +[source,modelica] +---- + Boolean b = time >= 0.5; + Clock c = Clock(b); + Boolean b2 = sample(b, c); +---- + +The first tick of `c` (the clock of `b2`) is at `0.5` where `b2` is `true`. +This is because both the event clock and sample introduce a delay of one event iteration, keeping them synchronized. +==== ++ +[NOTE] +The implicitly given interval and time shift can be modified by using the `subSample`, `superSample`, `shiftSample` and `backSample` operators on the returned clock, see <>, provided the base interval is not smaller than the implicitly given interval. + +[[operator:clock-solver]] +Opertor Clock:: ++ +[source,modelica] +---- +Clock(c=c, solverMethod=solverMethod) +---- ++ +_Solver clock_. +The first input argument, _c_, is a clock and the operator returns this clock. +The returned clock is associated with the second input argument _solverMethod_ of type `String`. +The meaning of _solverMethod_ is defined in <>. +If _solverMethod_ is the empty `String`, then this `Clock` construct does not associate an integrator with the returned clock. ++ +[example] +==== +Example: + +[source,modelica] +---- +Clock c1 = Clock(1, 10); // 100 ms, no solver +Clock c2 = Clock(c1, "ImplicitTrapezoid"); // 100 ms, ImplicitTrapezoid solver +Clock c3 = Clock(c2, ""); // 100 ms, no solver +---- +==== + +Besides inferred clocks and solver clocks, one of the following mutually exclusive associations of clocks are possible in one base-partition: + +. One or more periodic rational interval clocks, provided they are consistent with each other, see <>. ++ +[example] +==== +Assume `y = subSample(u)`, and `Clock(1, 10)` is associated with `u` and `Clock(2, 10)` is associated with `y`, then this is correct, but it would be an error if `y` is associated with a `Clock(1, 3)`. +==== + +. Exactly one non-periodic rational interval clock. + +. Exactly one real interval clock. ++ +[example] +==== +Assume `Clock c = Clock(2.5)`, then variables in the same base-partition can be associated multiple times with `c` but not multiple times with `Clock(2.5)`. +==== + +. Exactly one event clock. + +. A default clock, if neither a real interval, nor a rational interval nor an event clock is associated with a base-partition. + In this case the default clock is associated with the fastest sub-partition. ++ +[NOTE] +Typically, a tool will use `Clock(1.0)` as a default clock and will raise a warning, that it selected a default clock. + +Clock variables can be used in a restricted form of expressions. +Generally, every expression switching between clock variables must be an evaluable expression (in order that clock analysis can be performed when translating a model). +Thus subscripts on clock variables and conditions of if-then-else switching between clock variables must be evaluable expressions, and there are similar restrictions for sub-clock conversion operators <>. +Otherwise, the following expressions are allowed: + +* Declaring arrays of clocks. ++ +[example] +==== +Example: + +[source,modelica] +---- +Clock c1[3] = {Clock(1), Clock(2), Clock(3)} +---- +==== + +* Array constructors of clocks: `{}`, `[]`, `cat`. + +* Array access of clocks. ++ +[example] +==== +Example: + +[source,modelica] +---- +sample(u, c1[2]) +---- +==== + +* Equality of clocks. ++ +[example] +==== +Example: + +[source,modelica] +---- +c1 = c2 +---- +==== + +* `if`-expressions of clocks in equations. ++ +[example] +==== +Example: + +[source,modelica] +---- +Clock c2 = + if f > 0 then + subSample(c1, f) + elseif f < 0 then + superSample(c1, f) + else + c1; +---- +==== + +* Clock variables can be declared in models, blocks, connectors, and records. + A clock variable can be declared with the prefixes `input`, `output`, `inner`, `outer`, but _not_ with the prefixes `flow`, `stream`, `discrete`, `parameter`, or `constant`. ++ +[example] +==== +Example: + +[source,modelica] +---- +connector ClockInput = input Clock; +---- +==== + +=== Clocked State Variables + +Definition Clocked state variable:: +A component expression which is not a parameter, and to which `previous` has been applied. + +The previous value of a clocked variable can be accessed with the `previous` operator, listed below. + +[cols="1,1,1"] +|=== +|Expression |Description |Details + +|`previous(u)` +|Previous value of clocked variable +|<> +|=== + +[[modelica-previous]] +Operator previous:: ++ +[source,modelica] +---- +previous(u) +---- ++ +The input argument _u_ is a component expression (<>). +If _u_ is a parameter, its value is returned. ++ +Otherwise: Input and return arguments are on the same clock. +At the first tick of the clock of _u_ or after a reset transition (see <>), the start value of _u_ is returned, see <>. +At subsequent activations of the clock of _u_, the value of _u_ from the previous clock activation is returned. ++ +[NOTE] +At a clock tick only the (previous) values of the clocked state variables are needed to compute the new values of all clocked variables on that clock. +This roughly corresponds to state variables in continuous time. + +=== Partitioning Operators + +A set of _clock conversion operators_ together act as boundaries between different clock partitions. + +==== Base-Clock Conversion Operators + +The operators listed below convert between a clocked and an unclocked representation and vice versa. + +[cols="1,1,1"] +|=== +|Expression |Description |Details + +|`sample(u, clock)` +|Sample unclocked expression +|<> + +|`hold(u)` +|Zeroth order hold of clocked-time variable +|<> +|=== + +[[modelica-clocked-sample,Operator sample]] +Operator sample:: ++ +[source,modelica] +---- +sample(u, clock) +---- ++ +Input argument _u_ is in an unclocked base-partition, and there are no variability restrictions, i.e., it is continuous-time according to <>. +The optional input argument _clock_ is of type `Clock`, and can in a call be given as a named argument (with the name _clock_), or as positional argument. +The operator returns a clocked variable that has _clock_ as associated clock and has the value of the left limit of _u_ when _clock_ is active (that is the value of _u_ just before the event of _clock_ is triggered). +If _clock_ is not provided, it is inferred, see <>. ++ +[NOTE] +-- +Since the operator returns the left limit of _u_, it introduces an infinitesimal small delay between the unclocked and the clocked partition. +This corresponds to the reality, where a sampled data system cannot act infinitely fast and even for a very idealized simulation, an infinitesimal small delay is present. +The consequences for the sorting are discussed below. + +Input argument _u_ can be a general expression, because the argument is unclocked and therefore has always a value. +It can also be a constant, a parameter or a piecewise constant expression. + +Note that `sample` is an overloaded function: +If `sample` has two positional input arguments and the second argument is of type `Real`, it is the operator from <>. +If `sample` has one input argument, or it has two input arguments and the second argument is of type `Clock`, it is the base-clock conversion operator from this section. +-- + +[[modelica-hold,Operator hold]] +Operator hold:: ++ +[source,modelica] +---- +hold(u) +---- ++ +Input argument _u_ is a clocked (<>) component expression (<>) or a parameter expression. +The operator returns a piecewise constant signal of the same type as _u_. +When the clock of _u_ ticks, the operator returns _u_ and otherwise returns the value of _u_ from the last clock activation. +Before the first clock activation of _u_, the operator returns the start value of _u_, see <>. ++ +[NOTE] +Since the input argument is not defined before the first tick of the clock of _u_, the restriction is present, that it must be a component expression (or a parameter expression), in order that the initial value of _u_ can be used in such a case. + +[example] +==== +Example: Assume there is the following model: + +[source,modelica] +---- + Real y(start = 1), yc; +equation + der(y) + y = 2; + yc = sample(y, Clock(0.1)); +initial equation + der(y) = 0; +---- + +The value of `yc` at the first clock tick is `yc` = 2 (and not `yc` = 1). +The reason is that the continuous-time model `der(y) + y = 2` is first initialized and after initialization `y` has the value 2. +At the first clock tick at `time` = 0, the left limit of `y` is 2 and therefore `yc` = 2. +==== + +===== Sorting of a Simulation Model + +[NOTE] +-- +Since `sample(u)` returns the left limit of `u`, and the left limit of `u` is a known value, all inputs to a base-partition are treated as known during sorting. +Since a periodic and interval clock can tick at most once at a time instant, and since the left limit of a variable does not change during event iteration (i.e., re-evaluating a base-partition associated with a condition clock always gives the same result because the `sample(u)` inputs do not change and therefore need not to be re-evaluated), all base-partitions, see <>, need not to be sorted with respect to each other. +Instead, at an event instant, active base-partitions can be evaluated first (and once) in any order. +Afterwards, the unclocked base-partition is evaluated. + +Event iteration takes place only over the unclocked base-partition. +In such a scenario, accessing the left limit of `u` in `sample(u)` just means to pick the latest available value of `u` when the base-partition is entered, storing it in a local variable of the base-partition and only using this local copy during evaluation of the equations in this base-partition. +-- + +==== Sub-Clock Conversion Operators + +The operators listed below convert between synchronous clocks. + +[cols="1,1,1"] +|=== +|Expression |Description |Details + +|`subSample(u, factor)` +|Clock that is slower by a factor +|<> + +|`superSample(u, factor)` +|Clock that is faster by a factor +|<> + +|`shiftSample(u, shiftCounter, resolution)` +|Clock with time-shifted ticks +|<> + +|`backSample(u, backCounter, resolution)` +|Inverse of `shiftSample` +|<> + +|`noClock(u)` +|Clock that is always inferred +|<> +|=== + +These operators have the following properties: + +* The input argument _u_ is a clocked expression or an expression of type `Clock`. + (The operators can operate on all types of clocks.) + If _u_ is a clocked expression, the operator returns a clocked variable that has the same type as the expression. + If _u_ is an expression of type `Clock`, the operator returns a `Clock` -- except for `noClock` where it is an error. + +* The optional input arguments `factor` (defaults to 0, with `min = 0`), and `resolution` (defaults to 1, with `min = 1`) are evaluable expressions of type `Integer`. + +* Calls of the operators can use named arguments for the multi-letter arguments (i.e., not for _u_) with the given names, or positional arguments. ++ +[NOTE] +-- +Named arguments can make the calls easier to understand. +-- + +* The input arguments `shiftCounter` and `backCounter` are evaluable expressions of type `Integer` with `min = 0`. + +[[operator:subsample,Operator subSample]] +Operator subSample:: ++ +[source,modelica] +---- +subSample(u, factor=factor) +---- ++ +The clock of `y = subSample(u, factor)` is _factor_ times slower than the clock of _u_. +At every _factor_ ticks of the clock of _u_, the operator returns the value of _u_. +The first activation of the clock of `y` coincides with the first activation of the clock of _u_, and then every activation of the clock of `y` coincides with the every _factor_th activativation of the clock of _u_. +If _factor_ is not provided or is equal to zero, it is inferred, see <>. + +[[operator:supersample,Operator superSample]] +Operator superSample:: ++ +[source,modelica] +---- +superSample(u, factor=factor) +---- ++ +The clock of `y = superSample(u, factor)` is _factor_ times faster than the clock of _u_. +At every tick of the clock of `y`, the operator returns the value of _u_ from the last tick of the clock of _u_. +The first activation of the clock of `y` coincides with the first activation of the clock of _u_, and then the interval between activations of the clock of _u_ is split equidistantly into _factor_ activations, such that the activation 1 + _k_ · _factor_ of `y` coincides with the 1 + _k_ activation of _u_. ++ +[NOTE] +Thus `subSample(superSample(u, factor), factor)` = _u_. ++ +If _factor_ is not provided or is equal to zero, it is inferred, see <>. +If an event clock is associated to a base-partition, all its sub-partitions must have resulting clocks that are sub-sampled with an `Integer` factor with respect to this base-clock. ++ +[example] +==== +Example: + +[source,modelica] +---- +Clock u = Clock(x > 0); +Clock y1 = subSample(u, 4); +Clock y2 = superSample(y1, 2); // fine; y2 = subSample(u, 2) +Clock y3 = superSample(u, 2); // error +Clock y4 = superSample(y1, 5); // error +---- +==== + +[[operator:shiftsample,Operator shiftSample]] +Operator shiftSample:: ++ +[source,modelica] +---- +shiftSample(u, shiftCounter=k, resolution=resolution) +---- ++ +The operator `c = shiftSample(u, k, resolution)` splits the interval between ticks of _u_ into _resolution_ equidistant intervals _i_. +The clock `c` then ticks _k_ intervals _i_ after each tick of _u_. ++ +It leads to ++ +[source,modelica] +---- +shiftSample(u, k, resolution) = + subSample(shiftSample(superSample(u, resolution), k), resolution) +---- ++ +[NOTE] +-- +Note, due to the restriction of `superSample` on event clocks, `shiftSample` can only shift the number of ticks of the event clock, but cannot introduce new ticks. +Example: + +[source,modelica] +---- +// Rational interval clock +Clock u = Clock(3, 10); // ticks: 0, 3/10, 6/10, ... +Clock y1 = shiftSample(u, 1, 3); // ticks: 1/10, 4/10, ... +// Event clock +Integer revolutions = integer(time); +Clock u = Clock(change(revolutions), startInterval = 0.0); + // ticks: 0.0, 1.0, 2.0, 3.0, ... +Clock y1 = shiftSample(u, 2); // ticks: 2.0, 3.0, ... +Clock y2 = shiftSample(u, 2, 3); // error (resolution must be 1) +---- + +Additional example showing the full form: + +[source,modelica] +---- + Integer intervalCnt(start=2); + Integer cnt(start=0); + Clock u = Clock(intervalCnt,1); + Clock s1 = shiftSample(u, 3, 2); +equation + when u then + cnt = previous(cnt) + 1; + intervalCnt = if (cnt>=2) then 1 else previous(intervalCnt); + end when; +---- + +Here `u` ticks at 0, 2, 3, 4, 5, 6. +First you `superSample` to split each sampling interval in two equal parts leading to the ticks 0.0, 1.0, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0. +Then the simple `shiftSample` removes the first three ticks giving 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0. +And finally every other point is removed by `subSample`, and `s1` ticks at 2.5, 3.5, 4.5, 5.5. +-- + +[[operator:backsample,Operator backSample]] +Operator backSample:: ++ +[source,modelica] +---- +backSample(u, backCounter=cnt, resolution=res) +---- ++ +The input argument _u_ is either a component expression (<>) or an expression of type `Clock`. +This is an inverse of `shiftSample` such that `Clock y = backSample(u, cnt, res)` implicitly defines a clock `y` such that `shiftSample(y, cnt, res)` activates at the same times as _u_. +It is an error if the clock of `y` starts before the base-clock of _u_. ++ +At every tick of the clock of `y`, the operator returns the value of _u_ from the last tick of the clock of _u_. +If _u_ is a clocked component expression, the operator returns the start value of _u_, see <>, before the first tick of the clock of _u_. ++ +[example] +==== +Example: + +[source,modelica] +---- +// Rational interval clock 1 +Clock u = Clock(3, 10); // ticks: 0, 3/10, 6/10, ... +Clock y1 = shiftSample(u, 3); // ticks: 9/10, 12/10, ... +Clock y2 = backSample(y1, 2); // ticks: 3/10, 6/10, ... +Clock y3 = backSample(y1, 4); // error (ticks before u) +Clock y4 = shiftSample(u, 2, 3); // ticks: 2/10, 5/10, ... +Clock y5 = backSample(y4, 1, 3); // ticks: 1/10, 4/10, ... +// Event clock +Integer revolutions = integer(time); +Clock u = Clock(change(revolutions), startInterval = xx) + // ticks: 0, 1.0, 2.0, 3.0, ... +Clock y1 = shiftSample(u, 3); // ticks: 3.0, 4.0, ... +Clock y2 = backSample(y1, 2); // ticks: 1.0, 2.0, ... +---- +==== + +[[operator:noclock,Operator noClock]] +Operator noClock:: ++ +[source,modelica] +---- +noClock(u) +---- ++ +The clock of `y = noClock(u)` is always inferred, and _u_ must be part of the same base-clock as `y`. +At every tick of the clock of `y`, the operator returns the value of _u_ from the last tick of the clock of _u_. +If `noClock(u)` is called before the first tick of the clock of _u_, the start value of _u_ is returned. + +[NOTE] +-- +Clarification of `backSample`: + +Let _a_ and _b_ be positive integers with _a_ < _b_, and + +[source,modelica] +---- +yb = backSample(u, a, b) +ys = shiftSample(u, b-a, b) +---- + +Then when `ys` exists, also `yb` exists and `ys = yb`. + +The variable `yb` exists for the above parameterization with `a < b` one clock tick before `ys`. +Therefore, `backSample` is basically a `shiftSample` with a different parameterization and the clock of `backSample.y` ticks before the clock of `u`. +Before the clock of `u` ticks, `yb = u.start`. +-- + +[NOTE] +-- +Clarification of `noClock` operator: + +Note, that `noClock(u)` is not equivalent to `sample(hold(u))`. +Consider the following model: + +[source,modelica] +---- +model NoClockVsSampleHold + Clock clk1 = Clock(0.1); + Clock clk2 = subSample(clk1, 2); + Real x(start = 0), y(start = 0), z(start = 0); +equation + when clk1 then + x = previous(x) + 0.1; + end when; + when clk2 then + y = noClock(x); // most recent value of x + z = sample(hold(x)); // left limit of x (infinitesimally delayed)! + end when; +end NoClockVsSampleHold; +---- + +Due to the infinitesimal delay of `sample`, `z` will not show the current value of `x` as `clk2` ticks, but will show its previous value (left limit). +However, `y` will show the current value, since it has no infinitesimal delay. +-- + +Note that it is not legal to compute the derivative of the `sample`, `subSample`, `superSample`, `backSample`, +`shiftSample`, and `noClock` operators. + +=== Clocked When-Clause + +In addition to the previously discussed `when`-equation (see <>), a _clocked_ `when`-clause is introduced: + +[source,modelica] +---- +when clockExpression then + + ... +end when; +---- + +The clocked `when`-clause cannot be nested and does not have any `elsewhen` part. +It cannot be used inside an algorithm. +General equations are allowed in a clocked `when`-clause. + +For a clocked `when`-clause, all equations inside the `when`-clause are clocked with the same clock given by the _clockExpression_. + +=== Clock Partitioning + +This section defines how clock-partitions and clocks associated with equations are inferred. + +[NOTE] +==== +Typically clock partitioning is performed before sorting the equations. +The benefit is that clocking and symbolic transformation errors are separated. +==== + +Every clocked variable is uniquely associated with exactly one clock. + +After model flattening, every equation in an equation section, every expression and every algorithm section is either unclocked, or it is uniquely associated with exactly one clock. +In the latter case it is called a _clocked equation_, a _clocked expression_ or _clocked algorithm_ section respectively. +The associated clock is either explicitly defined by a `when`-clause, see <>, or it is implicitly defined by the requirement that a clocked equation, a clocked expression and a clocked algorithm section must have the same clock as the variables used in them with exception of the expressions used as first arguments in the conversion operators of <>. +_Clock inference_ means to infer the clock of a variable, an equation, an expression or an algorithm section if the clock is not explicitly defined and is deduced from the required properties in the previous two paragraphs. + +All variables in an expression without clock conversion operators must have the same clock to infer the clocks for each variable and expression. +The clock inference works both forward and backwards regarding the data flow and is also being able to handle algebraic loops. +The clock inference method uses the set of variable incidences of the equations, i.e., what variables that appear in each equation. + +Note that incidences of the first argument of clock conversion operators of <> are handled specially. + +[NOTE] +==== +As clock partitions are solely determined by the equations, two different clock partitions can have clocks defined by the same expressions. +It is a quality of implementation issue that such partitions are executed synchronously, e.g., by putting them in the same task in a real-time simulation context. +==== + +==== Flattening of Model + +The clock partitioning is conceptually performed after model flattening, i.e., redeclarations have been elaborated, arrays of model components expanded into scalar model components, and overloading resolved. +Furthermore, function calls to inline functions have been inlined. + +[NOTE] +==== +This is called _conceptually_, because a tool might do this more efficiently in a different way, provided the result is the same as if everything is flattened. +For example, array and matrix equations and records don't not need to be expanded if they have the same clock. +==== + +Furthermore, each non-trivial expression (non-literal, non-constant, non-parameter, non-variable), _expr~i~_, appearing as first argument of a clock conversion operator (except `hold` and `backSample`) is recursively replaced by a unique variable, _v~i~_, and the equation _v~i~_ = _expr~i~_ is added to the equation set. + +==== Connected Components of the Equations and Variables Graph + +Consider the set _E_ of equations and the set _V_ of unknown variables (not constants and parameters) in a flattened model, i.e., _M_ = ⟨_E_, _V_⟩. +The partitioning is described in terms of an undirected graph ⟨_N_, _F_⟩ with the nodes _N_ being the set of equations and variables, _N_ = _E_ ∪ _V_. +The set incidence(_e_) for an equation _e_ in _E_ is a subset of _V_, in general, the unknowns which lexically appear in _e_. +There is an edge in _F_ of the graph between an equation, _e_, and a variable, _v_, if _v_ ∈ incidence(_e_): + +[latexmath] +++++ +F = \{(e, v) : e \in E, v \in \operatorname{incidence}(e)\} +++++ + +A set of clock partitions is the _connected components_ (Wikipedia, _Connected components_) of this graph with appropriate definition of the incidence operator. + +A special case is the built-in variable `time` (see <>). +Each use of `time` is conceptually included as a separate variable in this analysis, _time~i~_ with `der(time~i~)` = 1. + +[NOTE] +==== +This means that `time` can be used in different partitions without any restrictions. +Additionally, it means that every sub-partition directly referencing `time` contains a call to `der`. +==== + +==== Base-Partitioning + +The goal is to identify all clocked equations and variables that should be executed together in the same task, as well as to identify the unclocked base-partition. + +The base-partitioning is performed with base-clock inference which uses the following incidence definition: + +incidence(_e_) = the _unknown_ variables, as well as variables `x` in `der(x)`, `pre(x)`, and `previous(x)`, which lexically appear in _e_ except as first argument of base-clock conversion operators: `sample` and `hold` and `Clock(condition=..., startInterval=...)`. + +The resulting set of connected components, is the partitioning of the equations and variables, _B~i~_ = ⟨_E~i~_, _V~i~_⟩, according to base-clocks and unclocked partitions. + +The base partitions are identified as _clocked_ or as _unclocked partitions_ according to the following properties: + +A variable `u` in `sample(u)`, a variable `y` in `y = hold(ud)`, and a variable `b` in `Clock(b, startInterval=...)` where the `Boolean` `b` is in an unclocked partition. + +Correspondingly, variables `u` and `y` in +`y = sample(uc)`, +`y = subSample(u)`, +`y = superSample(u)`, +`y = shiftSample(u)`, +`y = backSample(u)`, +`y = previous(u)`, +are in a clocked base-partition. +Equations in a clocked `when`-clause are also in a clocked base-partition. +Other base-partitions, where none of the variables in the partition are associated with any of the operators above, have an unspecified partition kind and are considered to be unclocked base-partitions. + +All unclocked base-partitions are collected together and form _the unclocked base-partition_. + +[example] +==== +Example: + +[source,modelica] +---- +// Controller 1 +ud1 = sample(y,c1); +0 = f1(yd1, ud1, previous(yd1)); + +// Controller 2 +ud2 = superSample(yd1,2); +0 = f2(yd2, ud2); + +// Unclocked system +u = hold(yd2); +0 = f3(der(x1), x1, u); +0 = f4(der(x2), x2, x1); +0 = f5(der(x3), x3); +0 = f6(y, x1, u); +---- + +After base-partitioning, the following partitions are identified: + +[source,modelica] +---- +// Base partition 1 -- clocked partition +ud1 = sample(y, c1); // incidence(e) = {ud1} +0 = f1(yd1, ud1, previous(ud1)); // incidence(e) = {yd1, ud1} +ud2 = superSample(yd1, 2); // incidence(e) = {ud2, yd1} +0 = f2(yd2, ud2); // incidence(e) = {yd2, ud2} + +// Base partition 2 -- unclocked partition +u = hold(yd2); // incidence(e) = {u} +0 = f3(der(x1), x1, u); // incidence(e) = {x1, u} +0 = f4(der(x2), x2, x1); // incidence(e) = {x2, x1} +0 = f6(y, x1, u); // incidence(e) = {y, x1, u} + +// Identified as separate partition, but belonging to base-partition 2 +0 = f5(der(x3), x3); // incidence(e) = {x3} +---- +==== + +==== Sub-Partitioning + +For each clocked base-partition B~i~, identified in <>, the sub-partitioning is performed with sub-clock inference which uses the following incidence definition: + +incidence(_e_) = the _unknown_ variables, as well as variables `x` in `der(x)`, `pre(x)`, and `previous(x)`, which lexically appear in _e_ except as first argument of sub-clock conversion operators: `subSample`, `superSample`, `shiftSample`, `backSample`, `noClock`, and `Clock` with first argument of `Boolean` type. + +The resulting set of connected components, is the partitioning of the equations and variables, _S~ij~_ = ⟨_E~ij~_, _V~ij~_⟩, according to sub-clocks. + +The connected components (corresponding to the sub-clocks) are then further split into strongly connected components corresponding to systems of equations. +The resulting sets of equations and variables shall be possible to solve separately, meaning that systems of equations cannot involve different sub-clocks. + +It can be noted that: + +[latexmath] +++++ +\begin{aligned} +E_{ij} \bigcap E_{kl} &= \emptyset,\, \forall i\ne{}k, j\ne{}l \\ +V_{ij} \bigcap V_{kl} &= \emptyset,\, \forall i\ne{}k, j\ne{}l \\ +V &= \bigcup V_{ij} \\ +E &= \bigcup E_{ij} +\end{aligned} +++++ + +[example] +==== +Example: After sub-partitioning of the example from <>, the following partitions are identified: + +[source,modelica] +---- +// Base partition 1 (clocked partition) +// Sub-partition 1.1 +ud1 = sample(y, c1); // incidence(e) = {ud1} +0 = f1(yd1, ud1, previous(yd1)); // incidence(e) = {yd1,ud1} + +// Sub-partition 1.2 +ud2 = superSample(yd1, 2); // incidence(e) = {ud2} +0 = f2(yd2, ud2); // incidence(e) = {yd2,ud2} + +// Base partition 2 (no sub-partitioning, since unclocked) +u = hold(yd2); +0 = f3(der(x1), x1, u); +0 = f4(der(x2), x2, x1); +0 = f5(der(x3), x3); +0 = f6(y, x1, u); +---- +==== + +[example] +==== +Example: Forbidding systems of equations involving different sub-clocks means that the following is forbidden: + +[source,modelica] +---- + Real a; + //Real x=a+z; + Real y=superSample(a+z, 2); + Real z; +equation + a+z = sample(time, Clock(1,100)); + 0 = subSample(y, 2)+a; +---- + +Here `a` and `z` are part of one sub-clock, and `y` of another, and the system of equations involve both of them. + +The following legal example solves the issues in the previous example by replacing `a` by `x-z` (and simplifying the equations). +Additionally, it shows that it is not required that the sub-clocks can necessarily be sorted: + +[source,modelica] +---- + Real x=sample(time, Clock(1,100)); + Real y=superSample(x, 2); + Real z=subSample(y, 2)+x; +---- + +Here `x` and `z` are part of one sub-partition, and `y` of another. +The equations form three equation systems with one equation in each (hence trivially satisfying the requirement that only variables from one sub-partition are being solved). +The equation systems need to be solved in a strict order, but the first and last equation system belong to one sub-clock, while the second equation system belongs to another sub-clock. +This illustrates that there is no guarantee that the sub-partitions can be ordered in agreement with the equation systems. +Note that equation systems with more than one equation are also allowed in sub-partitions. +==== + +==== Sub-Clock Inferencing + +For each base-partition, the base interval needs to be determined and for each sub-partition, the sub-sampling factors and shift need to be determined. +The sub-partition intervals are constrained by `subSample` and `superSample` factors which might be known (or evaluable expression) or unspecified, as well as by `shiftSample`, `shiftCounter` and `resolution`, or `backSample`, `backCounter` and `resolution`. +This constraint set is used to solve for all intervals and sub-sampling factors and shift of the sub-partitions. +The model is erroneous if no solution exist. + +[NOTE] +==== +It must be possible to determine that the constraint set is valid at compile time. +However, in certain cases, it could be possible to defer providing actual numbers until run-time. +==== + +It is required that accumulated sub- and supersampling factors in the range of 1 to 2^63^ can be handled. + +[NOTE] +==== +64 bit internal representation of numerator and denominator with sign can be used and gives minimum resolution 1.08×10^-19^ seconds and maximum range 9.22×10^18^ seconds = 2.92×10^11^ years. +==== + +=== Discretized Sub-Partition + +[NOTE] +==== +The goal is that every continuous-time Modelica model can be utilized in a sampled data control system. +This is achieved by solving the continuous-time equations with a defined integration method between clock ticks. +With this feature, it is for example possible to invert the nonlinear dynamic model of a plant, see <>, and use it in a feedforward path of an advanced control system that is associated with a clock. + +This feature also allows defining multi-rate systems: Different parts of the continuous-time model are associated to different clocks and are solved with different integration methods between clock ticks, e.g., a very fast sub-system with an implicit solver with a small step-size and a slow sub-system with an explicit solver with a large step-size. +==== + +With the language elements defined in this section, continuous-time equations can be used in clocked partitions. +Hereby, the continuous-time equations are solved with the defined integration method between clock ticks. + +Such a sub-partition is called a _discretized_ sub-partition, and the clock ticks are not interpreted as events, but as step-sizes of the integrator that the integrator must hit exactly. +Hence, no event handling is triggered at clock ticks (provided an explicit event is not triggered from the model at this time instant). + +[NOTE] +==== +The interpretation of the clock ticks is the same assumption as for manually discretized controllers, such as the z-transform. +==== + +[NOTE] +==== +It is not defined how to handle events that are triggered while solving a discretized sub-partition. +For example, a tool could handle events in the same way as for a usual simulation -- but only check them at the time associated with clock-ticks. + +Alternatively, relations might be interpreted literally, so that events are no longer triggered (in order that the time for an integration step is always the same, as needed for hard real-time requirements). +However, even if relations do not generate events, when-clauses and operators `edge` and `change` should behave as normal. +==== + +From the viewpoint of other partitions, the discretized continuous-time variables only have values at clock ticks (internally it may be more complicated, see <>). +Therefore, outside the discretized sub-partitions themselves, they are treated similarly to discrete-time sub-partitions. +Especially, operators such as `sample`, `hold`, `subSample` must be used to communicate signals of the discretized sub-partition with other partitions. + +==== Discrete-time and Discretized Sub-Partitions + +Additionally to the variability of expressions defined in <>, an orthogonal concept _clocked variability_ is defined in this section. +If not explicitly stated otherwise, an expression with a variability such as _continuous-time_ or _discrete-time_ means that the expression is inside a partition that is unclocked. +If an expression is present in a base-partition that is not an unclocked base-partition, it is a _clocked expression_ and has _clocked variability_. + +After sub-clock inferencing, see <>, every sub-partition that is associated with a clock has to be categorized as _discrete-time_ or _discretized_. + +[NOTE] +==== +Previously, discrete-time sub-partition was refered to as _clocked discrete-time partition_, and discretized sub-partition as _clocked discretized continuous-time partition_. +==== + +If a clocked sub-partition contains any of the operators `der`, `delay`, `spatialDistribution`, or event related operators from <> (with exception of `noEvent` and `smooth`), or contains a `when`-clause with a `Boolean` condition, it is a _discretized_ sub-partition. +Otherwise, it is a _discrete-time_ sub-partition. + +[NOTE] +==== +That is, a discrete-time sub-partition is a standard sampled data system described by difference equations. +==== + +A discretized sub-partition has to be solved with a _solver method_ of <>. +When `previous(x)` is used on a continuous-time state variable `x`, then `previous(x)` uses the start value of `x` as value for the first clock tick. + +The use of the operator `sample` from <> in a discretized sub-partition is problematic. +A diagnostic is recommended, especially if the operator is intended to generate events faster than the clock ticks, and otherwise the sampling should ideally be adjusted to the clock ticks. + +[NOTE] +==== +The reason for not disallowing `sample` in a discretized sub-partition is to make it possible to include _any_ continuous-time Modelica model in a sampled data control system. +Note that even if the sampling is slower than the clock ticks (or even the same rate) it still introduces the problem of possibly uneven sampling. +==== + +In a discrete-time sub-partition none of the event generating mechanisms apply. +Especially neither relations, nor any of the built-in operators of <> (event triggering mathematical functions) will trigger events. + +==== Solver Methods + +A sub-partition can have an integration method, directly associated (<>) or inferred from other sub-partitions (<>). +A predefined type `ModelicaServices.Types.SolverMethod` defines the methods supported by the respective tool by using the `choices` annotation. + +[NOTE] +==== +The `ModelicaServices` package contains tool specific definitions. +A string is used instead of an enumeration, since different tools might have different values and then the integer mapping of an enumeration is misleading since the same value might characterize different integrators. +==== + +The following names of solver methods are standardized: + +[source,modelica] +---- +type SolverMethod = String annotation(choices( + choice="External" "Solver specified externally", + choice="ExplicitEuler" "Explicit Euler method (order 1)", + choice="ExplicitMidPoint2" "Explicit mid point rule (order 2)", + choice="ExplicitRungeKutta4" "Explicit Runge-Kutta method (order 4)", + choice="ImplicitEuler" "Implicit Euler method (order 1)", + choice="ImplicitTrapezoid" "Implicit trapezoid rule (order 2)" +)) "Type of integration method to solve differential equations in a " + + "discretized sub-partition." +---- + +If a tool supports one of the integrators of `SolverMethod`, it must use the solver method name of above. + +[NOTE] +==== +A tool may also support other integrators. +Typically, a tool supports at least methods `"External"` and `"ExplicitEuler"`. +If a tool does not support the integration method defined in a model, typically a warning message is printed and the method is changed to `"External"`. +==== + +If the solver method is `"External"`, then the sub-partition associated with this method is integrated by the simulation environment for an interval of length of `interval()` using a solution method defined in the simulation environment. + +[NOTE] +==== +An example of such a solution method could be to have a table of the clocks that are associated with discretized sub-partitions and a method selection per clock. +In such a case, the solution method might be a variable step solver with step-size control that integrates between two clock ticks. +The simulation environment might also combine all partitions associated with method `"External"`, as well as all unclocked partitions, and integrate them together with the solver selected by the simulation environment. +==== + +If the solver method is _not_ `"External"`, then the sub-partition is integrated using the given method with the step-size `interval()`. + +[NOTE] +==== +For a periodic clock, the integration is thus performed with fixed step size. +==== + +The solvers are defined with respect to the underlying ordinary differential equation in state space form that corresponds to the sub-partition before it has been discretized, at least conceptually: + +[latexmath] +++++ +\begin{align*} +\dot{x} &= f(x, u, t)\\ +y &= g(x, u, t) +\end{align*} +++++ + +where: + +* _t_ is time +* _u~c~(t)_ is the continuous-time `Real` vector of input variables +* _u~d~(t)_ is the discrete-time `Real`/`Integer`/`Boolean`/`String` vector of input variables +* _x(t)_ is the continuous-time real vector of states +* _y(t)_ is the continuous-time or discrete-time `Real`/`Integer`/`Boolean`/`String` vector of algebraic and/or output variables + +A solver method is applied to a discretized sub-partition. +Such a partition has explicit inputs _u_ marked by `sample(u)`, `subSample(u)`, `superSample(u)`, `shiftSample(u)` and/or `backSample(u)`. +Furthermore, the outputs _y_ of such a partition are marked by `hold(y)`, `subSample(y)`, `superSample(y)`, `shiftSample(y)`, and/or `backSample(y)`. +The arguments of these operators are to be used as input signals _u_ and output signals _y_ in the conceptual ordinary differential equation above, and in the discretization formulae below, respectively. + +The solver methods (with exception of `"External"`) are defined by integrating from clock tick _t~i-1~_ to clock tick +_t~i~_ and computing the desired variables at _t~i~_, with _h_ = _t~i~_ - _t~i-1~_ = `interval`(_u_) and _x~i~_ = _x(t~i~)_ (for all methods: _y~i~_ = _g(x~i~,u~c,i~,u~d,i~,t~i~)_): + +[cols="1a,<3a"] +|=== +|SolverMethod |Solution method + +|`"ExplicitEuler"` +|[latexmath] +++++ +\begin{aligned} +x_{i} &:= x_{i-1}+h\cdot\dot{x}_{i-1}\\ +\dot{x}_{i} &:= f(x_i,u_{c,i},u_{d,i},t_i) +\end{aligned} +++++ + +|`"ExplicitMidPoint2"` +|[latexmath] +++++ +\begin{aligned} +x_{i} &:= x_{i-1}+h\cdot f(x_{i-1}+\frac{1}{2}\cdot h \cdot\dot{x}_{i-1},\frac{u_{c,i-1}+u_{c,i}}{2},u_{d,i-1},t_{i-1}+\tfrac{1}{2}\cdot h)\\ +\dot{x}_{i} &:= f(x_i,u_{c,i},u_{d,i},t_i) +\end{aligned} +++++ + +|`"ExplicitRungeKutta4"` +|[latexmath] +++++ +\begin{aligned} +k_1 &:= h\cdot \dot{x}_{i-1}\\ +k_2 &:= h\cdot f(x_{i-1}+\tfrac{1}{2}k_1,\frac{u_{c,i-1}+u_{c,i}}{2},u_{d,i-1},t_{i-1}+\tfrac{1}{2}\cdot h)\\ +k_3 &:= h\cdot f(x_{i-1}+\tfrac{1}{2}k_2,\frac{u_{c,i-1}+u_{c,i}}{2},u_{d,i-1},t_{i-1}+\tfrac{1}{2}\cdot h)\\ +k_4 &:= h\cdot f(x_{i-1}+k_3,u_{c,i},u_{d,i},t_i)\\ +x_{i} &:= x_{i-1}+\tfrac{1}{6}\cdot(k_1+2\cdot k_2+2\cdot k_3+k_4)\\ +\dot{x}_{i} &:= f(x_i,u_{c,i},u_{d,i},t_i) +\end{aligned} +++++ + +|`"ImplicitEuler"` +|[latexmath] +++++ +\text{Equation system with unknowns: }x_i, \dot{x}_i\\ +\begin{aligned} +x_{i} &= x_{i-1}+h\cdot\dot{x}_i\\ +\dot{x}_{i} &= f(x_i,u_{c,i},u_{d,i},t_i) +\end{aligned} +++++ + +|`"ImplicitTrapezoid"` +|[latexmath] +++++ +\text{Equation system with unknowns: }x_i, \dot{x}_i\\ +\begin{aligned} +x_{i} &= x_{i-1}+\tfrac{1}{2}h\cdot(\dot{x}_i+\dot{x}_{i-1})\\ +\dot{x}_{i} &= f(x_i,u_{c,i},u_{d,i},t_i) +\end{aligned} +++++ +|=== + +The initial conditions will be used at the first tick of the clock, and the first integration step will go from the first to the second tick of the clock. + +[example] +==== +Example: Assume the differential equation + +[source,modelica] +---- + input Real u; + Real x(start = 1, fixed = true); +equation + der(x) = -x + u; +---- + +shall be transformed to a discretized sub-partition with the `"ExplicitEuler"` method. +The following model is a manual implementation: + +[source,modelica] +---- + input Real u; + parameter Real x_start = 1; + Real x(start = x_start); // previous(x) = x_start at first clock tick + Real der_x(start = 0); // previous(der_x) = 0 at first clock tick +protected + Boolean first(start = true); +equation + when Clock() then + first = false; + if previous(first) then + // first clock tick (initialize system) + x = previous(x); + else + // second and further clock tick + x = previous(x) + interval() * previous(der_x); + end if; + der_x = -x + u; + end when; +---- +==== + +[NOTE] +For the implicit integration methods the efficiency can be enhanced by utilizing the discretization formula during the symbolic transformation of the equations. +For example, linear differential equations are then mapped to linear and not non-linear algebraic equation systems, and also the structure of the equations can be utilized. +For details see <>. +It might be necessary to associate additional data for an implicit integration method, e.g., the relative tolerance to solve the non-linear algebraic equation systems, or the maximum number of iterations in case of hard realtime requirements. +This data is tool specific and is typically either defined with a vendor annotation or is given in the simulation environment. + +==== Associating a Solver to a Sub-Partition + +A `SolverMethod` can be associated to a clock with the overloaded `Clock` constructor `Clock(c, solverMethod=...)`, see <>. +If a clock is associated with a sub-clock of a discretized sub-partition and a `SolverMethod` is associated with this clock, then the sub-partition is integrated with it. + +[example] +==== +Example: + +[source,modelica] +---- +// Continuous PI controller in a clocked partition +vd = sample(x2, Clock(Clock(1, 10), solverMethod="ImplicitEuler")); +e = ref - vd; +der(xd) = e / Ti; +u = k * (e + xd); + +// Physical model +f = hold(u); +der(x1) = x2; +m * der(x2) = f; +---- +==== + +==== Inferencing of solverMethod + +If a `solverMethod` is not explicitly associated with a sub-partition, it is inferred with a similar mechanism as for sub-clock inferencing, see <>. + +First, one set is constructed for each sub-partition, containing just this sub-partition. +These sets are then merged as follows: +For each set without a specified `solverMethod`, the set is merged with sets connected to it (these may contain a `solverMethod`), and this is repeated until it is not possible to merge more sets. +The sets connected in this way should be part of the same base-partition and connected through a sub-clock conversion operator (`subSample`, `superSample`, `shiftSample`, `backSample`, or `noClock`). + +* It is an error if this set contains multiple different values for `solverMethod`. + +* If the set contains continuous-time equations: + +** It is an error if this set contains no `solverMethod`. + +** Otherwise, the specified `solverMethod` is used. + +* If the set does not contain continuous-time equations, there is no need for a `solverMethod`. + However, inferencing between sub-partitions works the same regardless of whether there are continuous-time equations. + +[example] +==== +Example: + +[source,modelica] +---- +model InferenceTest + Real x(start = 3) "Explicitly using ExplicitEuler"; + Real y "Explicitly using ImplicitEuler method"; + Real z "Inferred to use ExplicitEuler"; +equation + der(x) = -x + sample(1, Clock(Clock(1, 10), solverMethod="ExplicitEuler")); + der(y) = subSample(x, 2) + + sample(1, Clock(Clock(2, 10), solverMethod="ImplicitEuler")); + der(z) = subSample(x, 2) + 1; +end InferenceTest; +---- + +Note that there is only one base-partition, but it has two different periodic rational clocks - consistent with <>. + +[source,modelica] +---- +model IllegalInference + Real x(start = 3) "Explicitly using ExplicitEuler"; + Real y "Explicitly using ImplicitEuler method"; + Real z; +equation + der(x) = -x + sample(1, Clock(Clock(1, 10), solverMethod="ExplicitEuler")); + der(y) = subSample(x, 2) + + sample(1, Clock(Clock(2, 10), solverMethod="ImplicitEuler")); + der(z) = subSample(x, 4) + 1 + subSample(y); +end IllegalInference; +---- + +Here `z` is a continuous-time equation connected directly to both `x` and `y` sub-partitions that have different `solverMethod`. +==== + +=== Initialization of Clocked Partitions + +The standard scheme for initialization of Modelica models does not apply for discrete-time sub-partitions. +Instead, initialization is performed in the following way: + +* Variables in discrete-time sub-partitions cannot be used in initial equation or initial algorithm sections. +* Attribute `fixed` cannot be applied to variables in discrete-time sub-partitions. + The attribute `fixed` is true for clocked states, otherwise false. + +=== Other Operators + +A few additional utility operators are listed below. + +[cols="a,a,a",options="autowidth"] +|=== +|Expression |Description |Details + +|`firstTick(u)` +|Test for first clock tick +|<> + +|`interval(u)` +|Interval between previous and present tick +|<> +|=== + +It is an error if these operators are called in the unclocked base-partition. + +[[modelica-firsttick,Operator firstTick]] +Operator firstTick:: ++ +[source,modelica] +---- +firstTick(u) +---- ++ +This operator returns true at the first tick of the clock of the expression, in which this operator is called. +The operator returns false at all subsequent ticks of the clock. +The optional argument _u_ is only used for clock inference, see <>. + +[[modelica-interval,Operator interval]] +Operator interval:: ++ +[source,modelica] +---- +interval(u) +---- ++ +This operator returns the interval between the previous and present tick of the clock of the expression, in which this operator is called. +The optional argument _u_ is only used for clock inference, see <>. +At the first tick of the clock the following is returned: ++ +. If the specified clock interval is a parameter expression, this value is returned. +. Otherwise the start value of the variable specifying the interval is returned. +. For an event clock the additional `startInterval` argument to the event clock constructor is returned. + ++ +The return value of `interval` is a scalar `Real` number. + +[example] +==== +Example: A discrete PI controller is parameterized with the parameters of a continuous PI controller, in order that the discrete block is robust against changes in the sample period. +This is achieved by discretizing a continuous PI controller (here with an implicit Euler method): + +[source,modelica] +---- +block ClockedPI + parameter Real T "Time constant of continuous PI controller"; + parameter Real k "Gain of continuous PI controller"; + input Real u; + output Real y; + Real x(start = 0); + protected + Real Ts = interval(u); +equation + /* Continuous PI equations: der(x) = u / T; y = k * (x + u); + * Discretization equation: der(x) = (x - previous(x)) / Ts; + */ + when Clock() then + x = previous(x) + Ts / T * u; + y = k * (x + u); + end when; +end ClockedPI; +---- + +A continuous-time model is inverted, discretized and used as feedforward controller for a PI controller (`der`, `previous`, `interval` are used in the same partition): + +[source,modelica] +---- +block MixedController + parameter Real T "Time constant of continuous PI controller"; + parameter Real k "Gain of continuous PI controller"; + input Real y_ref, y_meas; + Real y; + output Real yc; + Real z(start = 0); + Real xc(start = 1, fixed = true); + Clock c = Clock(Clock(0.1), solverMethod="ImplicitEuler"); +protected + Real uc; + Real Ts = interval(uc); +equation + /* Continuous-time, inverse model */ + uc = sample(y_ref, c); + der(xc) = uc; + /* PI controller */ + z = if firstTick() then 0 else + previous(z) + Ts / T * (uc - y_meas); + y = xc + k * (xc + uc); + yc = hold(y); +end MixedController; +---- +==== + +=== Semantics + +The execution of sub-partitions requires exact time management for proper synchronization. +The implication is that testing a `Real`-valued time variable to determine sampling instants is not possible. +One possible method is to use counters to handle sub-sampling scheduling, + +[source,modelica] +---- +Clock_i_j_ticks = + if pre(Clock_i_j_ticks) < subSamplingFactor_i_j then + 1 + pre(Clock_i_j_ticks) + else + 1; +---- + +and to test the counter to determine when the sub-clock is ticking: + +[source,modelica] +---- +Clock_i_j_activated = + BaseClock_i_activated and Clock_i_j_ticks >= subSamplingFactor_i_j; +---- + +The `Clock_i_j_activated` flag is used as the guard for the sub partition equations. + +[example] +==== +Consider the following example: + +[source,modelica] +---- +model ClockTicks + Integer second = sample(1, Clock(1)); + Integer seconds(start = -1) = mod(previous(seconds) + second, 60); + Integer milliSeconds(start = -1) = + mod(previous(milliSeconds) + superSample(second, 1000), 1000); + Integer minutes(start = -1) = + mod(previous(minutes) + subSample(second, 60), 60); +end ClockTicks; +---- + +A possible implementation model is shown below using Modelica 3.2 semantics. +The base-clock is determined to 0.001 seconds and the sub-sampling factors to 1000 and 60000. + +[source,modelica] +---- +model ClockTicksWithModelica32 + Integer second; + Integer seconds(start = -1); + Integer milliSeconds(start = -1); + Integer minutes(start = -1); + + Boolean BaseClock_1_activated; + Integer Clock_1_1_ticks(start = 59999); + Integer Clock_1_2_ticks(start = 0); + Integer Clock_1_3_ticks(start = 999); + Boolean Clock_1_1_activated; + Boolean Clock_1_2_activated; + Boolean Clock_1_3_activated; +equation + // Prepare clock tick + BaseClock_1_activated = sample(0, 0.001); + when BaseClock_1_activated then + Clock_1_1_ticks = + if pre(Clock_1_1_ticks) < 60000 then 1 + pre(Clock_1_1_ticks) else 1; + Clock_1_2_ticks = + if pre(Clock_1_2_ticks) < 1 then 1 + pre(Clock_1_2_ticks) else 1; + Clock_1_3_ticks = + if pre(Clock_1_3_ticks) < 1000 then 1 + pre(Clock_1_3_ticks) else 1; + end when; + Clock_1_1_activated = BaseClock_1_activated and Clock_1_1_ticks >= 60000; + Clock_1_2_activated = BaseClock_1_activated and Clock_1_2_ticks >= 1; + Clock_1_3_activated = BaseClock_1_activated and Clock_1_3_ticks >= 1000; + + // ----------------------------------------------------------------------- + // Sub partition execution + when {Clock_1_3_activated} then + second = 1; + end when; + when {Clock_1_1_activated} then + minutes = mod(pre(minutes) + second, 60); + end when; + when {Clock_1_2_activated} then + milliSeconds = mod(pre(milliSeconds) + second, 1000); + end when; + when {Clock_1_3_activated} then + seconds = mod(pre(seconds) + second, 60); + end when; +end ClockTicksWithModelica32; +---- +==== \ No newline at end of file diff --git a/docs/17__statemachines.adoc b/docs/17__statemachines.adoc new file mode 100644 index 000000000..edb9ae2ba --- /dev/null +++ b/docs/17__statemachines.adoc @@ -0,0 +1,682 @@ +== State Machines +:id: state-machines + +This chapter defines language elements to define clocked state machines. +These state machines have a similar modeling power as Statecharts (<>) and have the important feature that at one clock tick, there is only one assignment to every variable (for example, it is an error if state machines are executed in parallel and they assign to the same variable at the same clock tick; such errors are detected during translation). +Furthermore, it is possible to activate and deactivate clocked equations and blocks at a clock tick. +An efficient implementation will only evaluate the equations and blocks that are active at the current clock tick. +With other Modelica language elements, this important feature cannot be defined. + +The semantics of the state machines defined in this chapter is inspired by mode automata and is basically the one from Lucid Synchrone 3.0 (<>). +Note, safety critical control software in aircrafts is often defined with such kind of state machines. +The following properties are different to Lucid Synchrone 3.0: + +* Lucid Synchrone has two kinds of transitions: _strong_ and _weak_ transitions. +Strong transitions are executed before the actions of a state are evaluated and weak transitions are executed after the actions of a state are evaluated. +This can lead to surprising behavior, because the actions of a state are skipped if it is activated by a weak transition and exited by a true strong transition. ++ +For this reason, the state machines in this chapter use _immediate_ (= the same as _strong_) and _delayed_ transitions. +Delayed transitions are _immediate_ transitions where the condition is automatically delayed with an implicit `previous`. + +* Parallel state machines can be explicitly synchronized with a language element (similarly as parallel branches in Sequential Function Charts). +This often occurring operation can also be defined in Statecharts or in Lucid Synchrone state machines but only indirectly with appropriate conditions on transitions. + +* Modelica blocks can be used as states. +They might contain clocked equations. +If the equations are discretized, they are integrated between the previous and the current clock tick, if the corresponding state is active. + +=== Transitions + +Any Modelica block instance without continuous-time equations or continuous-time algorithms can potentially be a state of a state machine. +A cluster of instances which are coupled by `transition` statements makes a state machine. +All parts of a state machine must have the same clock. +All transitions leaving one state must have different priorities. +One and only one instance in each state machine must be marked as initial by appearing in an `initialState` statement. + +The special kinds of connect-like equations listed below are used to define a state machine. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`transition(from, to, condition, ...)` |State machine transition between states |<> +|`initialState(state)` |State machine initial state |<> +|=== + +The `transition` and `initialState` equations can only be used in equations, and cannot be used inside `if`-equations with conditions that are not parameter expressions, or in `when`-equations. + +The operators listed below are used to query the status of the state machine. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`activeState(state)` |Predicate for active state |<> +|`ticksInState()` |Ticks since activation |<> +|`timeInState()` |Time since activation |<> +|=== + +[[operator:transition,Operator transition]] +Operator transition:: ++ +[source,modelica] +---- +transition(from, to, condition, + immediate=imm, reset=reset, synchronize=synch, priority=prio) +---- ++ +Arguments `from` and `to` are block instances, and `condition` is a `Boolean` argument. +The optional arguments `immediate`, `reset`, and `synchronize` are of type `Boolean`, have parameter variability and default to `true`, `true`, and `false` respectively. +The optional argument `priority` is of type `Integer`, has parameter variability and a default of 1. ++ +This operator defines a transition from instance `from` to instance `to`. +The `from` and `to` instances become states of a state machine. +The transition fires when `condition = true` if `imm = true` (this is called an _immediate transition_) or `previous(condition)` when `imm = false` (this is called a _delayed transition_). +Argument `priority` defines the priority of firing when several transitions could fire. +In this case the transition with the smallest value of `priority` fires. +It is required that `prio >= 1` and that for all transitions from the same state, the priorities are different. +If `reset = true`, the states of the target state are reinitialized, i.e., state machines are restarted in initial state and state variables are reset to their start values. +If `synch = true`, any transition is disabled until all state machines of the from-state have reached final states, i.e., states without outgoing transitions. +For the precise details about firing a transition, see <>. + +[[operator:initialState,Operator initialState]] +Operator initialState:: ++ +[source,modelica] +---- +initialState(state) +---- ++ +Argument `state` is the block instance that is defined to be the initial state of a state machine. +At the first clock tick of the state machine, this state becomes active. + +[[operator:activeState,Operator activeState]] +Operator activeState:: ++ +[source,modelica] +---- +activeState(state) +---- ++ +Argument `state` is a block instance. +The operator returns `true` if this instance is a state of a state machine and this state is active at the actual clock tick. +If it is not active, the operator returns `false`. ++ +It is an error if the instance is not a state of a state machine. + +[[operator:ticksInState,Operator ticksInState]] +Operator ticksInState:: ++ +[source,modelica] +---- +ticksInState() +---- ++ +Returns the number of ticks of the clock of the state machine for which the currently active state has maintained its active state without interruption, i.e., without local or hierarchical transitions from this state. +In the case of a self-transition to the currently active state or to an active enclosing state, the number is reset to one. ++ +This function can only be used in state machines. + +[[operator:timeInState,Operator timeInState]] +Operator timeInState:: ++ +[source,modelica] +---- +timeInState() +---- ++ +Returns the time duration as `Real` in [s] for which the currently active state has maintained its active state without interruption, i.e., without local or hierarchical transitions from this state. +In the case of a self-transition to the currently active state or to an active enclosing state, the time is reset to zero. ++ +This function can only be used in state machines. + +[example] +==== +Example: If there is a transition with `immediate = false` from state `A1` to `A2` and the condition is `ticksInState() >= 5`, and `A1` became active at 10ms, and the clock period is 1ms, then `A1` will be active at 10ms, 11ms, 12ms, 13ms, 14ms, and will be not active at 15ms. + +[source,modelica] +---- +block State end State; +State A0; +State A1; // Becomes active at 10ms +State A2; +equation + initialState(A0); + transition(A0, A1, sample(time, Clock(1, 1000)) > 0.0095); + transition(A1, A2, ticksInState() >= 5, immediate = false); +---- +==== + +=== State Machine Graphics + +[NOTE] +<> shows the recommended layout of a state machine. + +.Recommended layout of a simple state machine. For the 5 transitions, the settings are as follows, from left to right: `immediate = true, false, true, false, true`; `reset = true, true, false, false, true`; `synchronize = false, false, false, false, true`; `priority = 1, 2, 3, 4, 5`. +[[fig-state-machine-layout]] +image::media/statemachine.svg[] + +The annotation for graphics of `transition` has the following structure: `annotation(Line(...), Text(...))`; and for `initialState()`: `graphical-primitives(Line(...))`; with `Line` and `Text` annotations defined in <>. + +[example] +==== +Example: + +[source,modelica] +---- +transition(state2, state1, x < 10, + immediate = true, reset = true, synchronize = false, priority = 1) + annotation( + Line( + points = {{-40,-16},{-36,-4},{-32,8},{-40,26},{-40,32},{-46,50}}, + color = {175, 175, 175}, + thickness = 0.25, + smooth = Smooth.Bezier), + Text( + string = "%condition", + extent = {{4, -4}, {4, -10}}, + fontSize = 10, + textStyle = {TextStyle.Bold}, + textColor = {95, 95, 95}, + horizontalAlignment = TextAlignment.Left), + ); +---- +==== + +The `Text` annotation representing the transition condition can use the notation `%condition` to refer to the condition expression. + +The extent of the Text is interpreted relative to either the first point of the `Line`, in the case of `immediate = false`, or the last point (`immediate = true`). + +In addition to the line defined by the points of the `Line` annotation, a perpendicular line is used to represent the transition. +This line is closer to the first point if `immediate = false` otherwise closer to the last point. + +If the condition text is somewhat distant from the perpendicular line, a dimmed straight line joins the transition text and the perpendicular line. +(See the rightmost transition above.) + +If `reset = true`, a filled arrow head is used otherwise an open arrow head. +For `synchronize = true`, an inverse "fork" symbol is used in the beginning of the arrow. +(See the rightmost transition above.) + +The value of the `priority` attribute is prefixing the condition text followed by a colon if `priority > 1`. + +The `initialState` line has a filled arrow head and a bullet at the opposite end of the initial state (as shown above). + +=== State Machine Semantics + +For the purpose of defining the semantics of state machines, assume that the data of all transitions are stored in an array of records: + +[source,modelica] +---- +record Transition + Integer from; + Integer to; + Boolean immediate = true; + Boolean reset = true; + Boolean synchronize = false; + Integer priority = 1; +end Transition; +---- + +The transitions are sorted with lowest priority number last in the array; and the priorities must be unique for each value of `from`. +The states are enumerated from 1 and up. +The transition conditions are stored in a separate array `c[:]` since they are time varying. + +The semantics model is a discrete-time system with inputs `{c[:], active, reset}` with `t` being an array corresponding to the inputs to the transition operator, outputs `{activeState, activeReset, activeResetStates[:]}` and states `{nextState, nextReset, nextResetStates[:]}`. +For a top-level state machine, active is always true. +For sub-state machines, active is true only when the parent state is active. +For a top-level state machine, reset is true at the first activation only. +For sub-state machine, reset is propagated from the state machines higher up. + +==== State Activation + +The state update starts from `nextState`, i.e., what has been determined to be the next state at the previous time. +`selectedState` takes into account if a reset of the state machine is to be done. + +[source,modelica] +---- +output Integer selectedState = if reset then 1 else previous(nextState); +---- + +The integer `fired` is calculated as the index of the transition to be fired by checking that `selectedState` is the from-state and the condition is true for an immediate transition or `previous(condition)` is true for a delayed transition. +The max function returns the index of the transition with highest priority or 0. + +[source,modelica] +---- +Integer fired = + max( + if t[i].from == selectedState and + (if t[i].immediate then c[i] else previous(c[i])) + then i + else 0 + for i in 1 : size(t, 1)); +---- + +The start value of c is false. +This definition would require that the previous value is recorded for all transitions conditions. +Below is described an equivalent semantics which just require to record the value of one integer variable delayed. + +The integer `immediate` is calculated as the index of the immediate transition to potentially be fired by checking that `selectedState` is the from-state and the condition is true. +The max function returns the index of the transition with true condition and highest priority or 0. + +[source,modelica] +---- +Integer immediate = + max( + if t[i].immediate and t[i].from == selectedState and c[i] then i else 0 + for i in 1 : size(t, 1)); +---- + +In a similar way, the `Integer delayed` is calculated as the index for a potentially delayed transition, i.e., a transition taking place at the next clock tick. +In this case the from-state needs to be equal to `nextState`: + +[source,modelica] +---- +Integer delayed = + max( + if not t[i].immediate and t[i].from == nextState and c[i] then i else 0 + for i in 1 : size(t, 1)); +---- + +The transition to be fired is determined as follows, taking into account that a delayed transition might have higher priority than an immediate: + +[source,modelica] +---- +Integer fired = max(previous(delayed), immediate); +---- + +`nextState` is set to the found transitions to-state: + +[source,modelica] +---- +Integer nextState = + if active then + (if fired > 0 then t[fired].to else selectedState) + else + previous(nextState); +---- + +In order to define synchronize transitions, each state machine must determine which are the final states, i.e., states without from-transitions and to determine if the state machine is in a final state currently: + +[source,modelica] +---- +Boolean finalStates[nStates] = + {min(t[j].from <> i for j in 1 : size(t, 1)) for i in 1 : nStates}; +Boolean stateMachineInFinalState = finalStates[activeState]; +---- + +To enable a synchronize transition, all the `stateMachineInFinalState` conditions of all state machines within the meta state must be true. +An example is given below in the semantic example model. + +==== Reset Handling + +A state can be reset for two reasons: + +* The whole state machine has been reset from its context. + In this case, all states must be reset, and the initial state becomes active. +* A reset transition has been fired. + Then, its target state is reset, but not other states. + +The first reset mechanism is handled by the `activeResetStates` and `nextResetStates` vectors. + +The state machine reset flag is propagated and maintained to each state individually: + +[source,modelica] +---- +output Boolean activeResetStates[nStates] = + {reset or previous(nextResetStates[i]) for i in 1 : nStates}; +---- + +until a state is eventually executed, then its corresponding reset condition is set to false: + +[source,modelica] +---- +Boolean nextResetStates[nStates] = + if active then + {activeState <> i and activeResetStates[i] for i in 1 : nStates} + else + previous(nextResetStates) +---- + +The second reset mechanism is implemented with the `selectedReset` and `nextReset` variables. +If no reset transition is fired, the `nextReset` is set to false for the next cycle. + +==== Activation Handling + +When a state is suspended, its equations should not be executed, and its variables keep their values -- including state-variables in discretized equations. + +The execution of a sub-state machine has to be suspended when its enclosing state is not active. +This activation flag is given as a `Boolean` input `active`. +When this flag is true, the sub-state machine maintains its previous state, by guarding the equations of the state variables `nextState`, `nextReset` and `nextResetStates`. + +==== Semantics Summary + +The entire semantics model is given below: + +[source,modelica] +---- +model StateMachineSemantics "Semantics of state machines" + parameter Integer nStates; + parameter Transition t[:] "Array of transition data sorted in priority"; + input Boolean c[size(t, 1)] "Transition conditions sorted in priority"; + input Boolean active "true if the state machine is active"; + input Boolean reset "true when the state machine should be reset"; + Integer selectedState = if reset then 1 else previous(nextState); + Boolean selectedReset = reset or previous(nextReset); + // For strong (immediate) and weak (delayed) transitions + Integer immediate = + max( + if (t[i].immediate and t[i].from == selectedState and c[i]) then i else 0 + for i in 1 : size(t, 1)); + Integer delayed = + max( + if (not t[i].immediate and t[i].from == nextState and c[i]) then i else 0 + for i in 1 : size(t, 1)); + Integer fired = max(previous(delayed), immediate); + output Integer activeState = + if reset then 1 elseif fired > 0 then t[fired].to else selectedState; + output Boolean activeReset = + reset or (if fired > 0 then t[fired].reset else selectedReset); + + // Update states + Integer nextState = if active then activeState else previous(nextState); + Boolean nextReset = not active and previous(nextReset); + // Delayed resetting of individual states + output Boolean activeResetStates[nStates] = + {reset or previous(nextResetStates[i]) for i in 1 : nStates}; + Boolean nextResetStates[nStates] = + if active then + {activeState <> i and activeResetStates[i] for i in 1 : nStates} + else + previous(nextResetStates); + Boolean finalStates[nStates] = + {min(t[j].from <> i for j in 1 : size(t, 1)) for i in 1 : nStates}; + Boolean stateMachineInFinalState = finalStates[activeState]; +end StateMachineSemantics; +---- + +==== Merging Variable Definitions + +[NOTE] +When a state class uses an `outer output` declaration, the equations have access to the corresponding variable declared `inner`. +Special rules are then needed to maintain the single assignment rule since multiple definitions of such outer variables in different mutually exclusive states needs to be merged. + +In each state, the outer output variables are solved for and for each such variable a single definition is formed: + +[source,modelica] +---- +v := + if activeState(state1) then + expre1 + elseif activeState(state2) then + expre2 + elseif ... + else + last(v) +---- + +`last` is a special internal semantic operator returning its input. +It is just used to mark for the sorting that the incidence of its argument should be ignored. +A start value must be given to the variable if not assigned in the initial state. + +A new assignment equation is formed which might be merged on higher levels in nested state machines. + +==== Merging Connections to Outputs + +[NOTE] +The causal connection semantics of Modelica for non-state machines are generalized to states of state machines, using the fact that only one state is active at a time. + +It is possible to connect outputs each coming from different states of state machines together -- and connect this with other causal connectors. +These outputs are combined seen as one source of the signal, and give the following constraint equations, + +[source,modelica] +---- +u1 = u2 = ... = y1 = y2 = ... +---- + +with `yi` being outputs from different states of the state-machine and `ui` being other causal variables. +The semantics is defined similarly to <>: + +[source,modelica] +---- +v = if activeState(state1) then + y1 + elseif activeState(state2) then + y2 + elseif ... + else + last(v); +u1 = v +u2 = v +... +---- + +=== Example + +.Example of a hierarchical state machine. +[[fig-hierarchical-statemachine]] +image::media/hierarchical-statemachine.svg[width=80%] + +[example] +==== +Example: Consider the hierarchical state machine in <>. +The model demonstrates the following properties: + +* `state1` is a meta state with two parallel state machines in it. +* `stateA` declares `v` as `outer output`. + `state1` is on an intermediate level and declares `v` as `inner outer output`, i.e., matches lower level `outer v` by being `inner` and also matches higher level `inner v` by being `outer`. + The top level declares `v` as `inner` and gives the start value. +* `count` is defined with a start value in `state1`. + It is reset when a reset transition (`v >= 20`) is made to `state1`. +* `stateX` declares the local variable `w` to be equal to `v` declared as `inner input`. +* `stateY` declares a local counter `j`. + It is reset at start and as a consequence of the reset transition (`v >= 20`) to `state1`: When the reset transition (`v >= 20`) fires, then the variables of the active states are reset immediately (so `count` from `state1`, and `i` from `stateX`). + The variables of other states are only reset at the time instants when these states become active. + So `j` in `StateY` is reset to 0, when the transition `stateX.i > 20` fires (after `state1` became active again, so after the reset transition `v >= 20`). +* Synchronizing the exit from the two parallel state machines of `state1` is done by checking that `stated` and `stateY` are active using the `activeState` function. + +The Modelica code (without annotations) is: + +[source,modelica] +---- +block HierarchicalAndParallelStateMachine + inner Integer v(start = 0); + + State1 state1; + State2 state2; +equation + initialState(state1); + transition(state1, state2, + activeState(state1.stateD) and activeState(state1.stateY), + immediate = false); + transition(state2, state1, v >= 20, immediate = false); + +public + block State1 + inner Integer count(start = 0); + inner outer output Integer v; + + block StateA + outer output Integer v; + equation + v = previous(v) + 2; + end StateA; + StateA stateA; + + block StateB + outer output Integer v; + equation + v = previous(v) - 1; + end StateB; + StateB stateB; + + block StateC + outer output Integer count; + equation + count = previous(count) + 1; + end StateC; + StateC stateC; + + block StateD + end StateD; + StateD stateD; + + equation + initialState(stateA); + transition(stateA, stateB, v >= 6, immediate = false); + transition(stateB, stateC, v == 0, immediate = false); + transition(stateC, stateA, true, immediate = false, priority = 2); + transition(stateC, stateD, count >= 2, immediate = false); + + public + block StateX + outer input Integer v; + Integer i(start = 0); + Integer w; // = v; + equation + i = previous(i) + 1; + w = v; + end StateX; + StateX stateX; + + block StateY + Integer j(start = 0); + equation + j = previous(j) + 1; + end StateY; + StateY stateY; + + equation + initialState(stateX); + transition(stateX, stateY, stateX.i > 20, + immediate = false, reset = false); + end State1; + + block State2 + outer output Integer v; + equation + v = previous(v) + 5; + end State2; +end HierarchicalAndParallelStateMachine; +---- + +.State machine behavior, as reflected by the variable v. +image::media/statemachineplot.svg[width=50%] + +The transition from `state1` to `state2` could have been done with a `synchronize` transition with `condition=true` instead. +The semantically equivalent model is shown below: + +[source,modelica] +---- +block HierarchicalAndParallelStateMachine + extends StateMachineSemantics( + nStates = 2, + t = {Transition(from = 1, to = 2, immediate = false, synchronize = true), + Transition(from = 2, to = 1, immediate = false)}, + c = {true, v >= 20}); + Boolean init(start = true) = sample(false); + + block State1 + Boolean active; + Boolean reset; + outer input Integer v_previous; + inner output Integer v; + inner Integer count(start = 0); + inner Integer count_previous = if reset then 0 else previous(count); + + block StateMachineOf_stateA + extends StateMachineSemantics( + nStates = 4, + t = {Transition(from = 1, to = 2, immediate = false), + Transition(from = 2, to = 3, immediate = false), + Transition(from = 3, to = 1, immediate = false), + Transition(from = 3, to = 4, immediate = false)}, + c = {v >= 6, v == 0, true, count >= 2}); + outer input Integer v_previous; + outer output Integer v; + outer input Integer count_previous; + outer output Integer count; + equation + inFinalState = true; // no macro states + if activeState == 1 then + // equations for stateA + v = v_previous + 2; + count = count_previous; + elseif activeState == 2 then + // equations for stateB + v = v_previous - 1; + count = count_previous; + elseif activeState == 3 then + // equations for stateC + v = v_previous; + count = count_previous + 1; + else // if activeState == 4 then + // equations for stateD + v = v_previous; + count = count_previous; + end if; + end StateMachineOf_stateA; + + StateMachineOf_stateA stateMachineOf_stateA( + active = active, reset = reset); + + block StateMachineOf_stateX + extends StateMachineSemantics( + nStates = 2, + t = {Transition(from = 1, to = 2, immediate = false, reset = false)}, + c = {i > 25}); + outer input Integer v; + Integer i(start = 0); + Integer i_previous; + Integer j(start = 0); + Integer j_previous; + Integer w; + equation + inFinalState = true; // no macro states + if activeState == 1 then + // equations for stateX + i_previous = + if activeReset or activeResetStates[1] then 0 else previous(i); + j_previous = previous(j); + i = i_previous + 1; + w = v; + j = j_previous; + else // if activeState == 2 then + // equations for stateY + i_previous = previous(i); + j_previous = + if activeReset or activeResetStates[2] then 0 else previous(j); + i = i_previous; + w = previous(w); + j = j_previous + 1; + end if; + end StateMachineOf_stateX; + + StateMachineOf_stateX stateMachineOf_stateX( + active = active, reset = reset); + Boolean inFinalState = + stateMachineOf_stateA.stateMachineInFinalState and + stateMachineOf_stateX.stateMachineInFinalState; + end State1; + + State1 state1; + Integer v(start = 0); + inner Integer v_previous = if reset then 0 else previous(v); +equation + active = true; + reset = previous(init); + if activeState == 1 then + // equations for state1 + inFinalState = state1.inFinalState; + state1.active = true; + state1.reset = activeReset or activeResetStates[1]; + v = state1.v; + else // if activeState == 2 then + // equations for state2 + inFinalState = true; // not macro state + state1.active = false; + state1.reset = false; + v = previous(v) + 5; + end if; +end HierarchcialAndParallelStateMachine; +---- +==== \ No newline at end of file diff --git a/docs/18__annotations.adoc b/docs/18__annotations.adoc new file mode 100644 index 000000000..e00566d43 --- /dev/null +++ b/docs/18__annotations.adoc @@ -0,0 +1,2766 @@ +== Annotations + +Annotations are intended for storing extra information about a model, such as graphics, documentation or versioning, etc. +The standard annotations (that is, all annotations except the vendor-specific ones, see <>) shall only be used where their semantics is defined. +A Modelica tool is free to define and use other annotations, in addition to those defined here, according to <>. + +Annotations are optional in the Modelica grammar, and when present, indicated using the `annotation` keyword, see `annotation-clause` in the grammar (<>). +The structure of the annotation content is the same as a class modification (`class-modification` in the grammar). +(For replaceable class declarations with a `constraining-clause` also refer to <>.) +The specification in this document defines the semantic meaning if a tool implements any of these annotations. + +=== Notation for Annotation Definitions + +Annotations are defined using the syntactic forms of Modelica component declarations and record definitions, with the following special semantics. +If the annotation is described by a component declaration, the annotation is used in the form of a value modifier for the same name. +If the annotation is described by a `record` _class_ the annotation is used in the form of a modifier for a `record` _component_ with the same name. + +A declaration equation for a component or record member specifies a default to be used when no corresponding annotation is given. +An array record member without declaration equation and with size specified by colon (`:`) defaults to being empty. +Default behavior can also be specified in the text as an alternative to a declaration equation, or implicitly by saying _if specified_. +If the description states that the annotation only has an effect for specific values it implies that the default is a value that has no effect. +When there is no declaration equation and no other explanation of default behavior in the text for a record member, an annotation modifier for the record must contain a modifier for that member. + +As all annotations are optional, component declaration annotations will always have default behavior. +The default behavior is either the implicit absence of any of the effects for defined values of the annotation, or explicitly defined as corresponding to one of the valid values. + +When an annotation is defined with a component variability prefix (<>), this restricts the allowed variability of corresponding annotation modifiers analogously to the rules in <>. +If the annotation is declared as an `+/*evaluable*/ parameter+` the corresponding modifier is further restricted to be evaluable. +If the annotation is declared as a `+/*literal*/ constant+` the corresponding modifier is further restricted to be a literal value. + +[example] +==== +Example: Since the semantics of an annotation comes from the interpretation as a modification, empty annotations have no impact at all. +For example, the following is allowed, but meaningless: + +[source,modelica] +---- +annotation(Dialog()); +---- + +An annotation such as `annotation(Dialog)` is also meaningless, but deprecated according to <>. +==== + +=== Semantic Restrictions of Annotation Syntax + +The syntactic form of annotations, `annotation-clause`, uses the generic `class-modification` in <>. +However, except where explicitly stated, the following constructs shall not be used in annotations: + +* `final`. For instance, neither `final experiment(StopTime = 2.0)` nor `experiment(final StopTime = 2.0)` may be used to prevent that an extending model overrides the `StopTime` setting. + +* `each`. When an annotation is given for an array component declaration, it applies to the array as a whole. Thus, neither should values be given in arrays matching the size of the declared component, nor should `each` be used express that a scalar value applies to each element of the array. + +* `element-redeclaration` in the grammar. In particular, the keyword `redeclare` cannot be used. + +* `element-replaceable` in the grammar. In particular the keywords `replaceable` and `constrainedby` cannot be used. + +* An `element-modification` without `modification` is deprecated without exceptions. The meaning of such annotations has never been defined. + +[example] +==== +Example: The following annotations are not merely meaningless, but deprecated: + +[source,modelica] +---- +annotation( + Dialog, + Dialog(colorSelector) +); +---- + +In particular, the effect of `colorSelector` is not the same as `colorSelector = true`, even though the latter is the only meaningful use of `colorSelector`. +==== + +=== Expression Evaluation Inside Annotations + +This section describes some differences to the evaluation of expressions inside of annotations compared to normal evaluation rules outside of annotations. + +==== Enumerations for Use in Annotations + +Several annotations make use of dedicated enumeration types. +These enumeration types do not have the full status of being built-in types as, e.g., `StateSelect` (<>). +Instead, they are only in scope where expressions inside annotations are evaluated, shadowing any user-defined definitions with the same names. + +[example] +==== +Example: The `smooth` attribute of a `Polygon` can be controlled through a model parameter, but the parameter cannot use the `Smooth` type directly: + +[source,modelica] +---- +model BezierParameter + parameter Smooth smooth = Smooth.Bezier; // Error: Smooth is unknown here. + parameter Boolean bezier = true; // Fine. + annotation(Icon(graphics = { + Polygon( + smooth = if bezier then Smooth.Bezier else Smooth.None, + points = {{-50, -20}, {0, 30}, {50, -20}} + ) + })); +end BezierParameter; +---- +==== + +[NOTE] +==== +When evaluating the expression _expr_ in the model `M`, one can imagine it being done as in + +[source,modelica] +---- +model M_annotation + extends M; + import AnnotationEnumerations.*; + T result = expr; +end M_annotation; +---- + +where `AnnotationEnumerations` is a hidden package containing all enumeration types used in annotations, and _T_ is the expected type of the expression. +==== + +=== Vendor-Specific Annotations + +A vendor may -- anywhere inside an annotation -- add specific, possibly undocumented, annotations which are not intended to be interpreted by other tools. +The semantic restrictions in <> are not enforced in vendor-specific annotations, giving vendors the full freedom of using the most general form of annotations. +The only requirement is that any tool shall save files with all vendor-specific annotations (and all annotations from this chapter) intact. +Two variants of vendor-specific annotations exist; one simple and one hierarchical. +Double underscore concatenated with a vendor name as initial characters of the identifier are used to identify vendor-specific annotations. + +[example] +==== +Example: + +[source,modelica] +---- +annotation( + Icon(coordinateSystem(extent = {{-100, -100}, {100, 100}}), + graphics = {__NameOfVendor(Circle(center = {0, 0}, radius = 10))})); +---- + +This introduces a new graphical primitive `Circle` using the hierarchical variant of vendor-specific annotations. + +[source,modelica] +---- +annotation( + Icon(coordinateSystem(extent = {{-100, -100}, {100, 100}}), + graphics = {Rectangle(extent = {{-5, -5}, {7, 7}}, + __NameOfVendor_shadow = 2)})); +---- + +This introduces a new attribute `__NameOfVendor_shadow` for the `Rectangle` primitive using the simple variant of vendor-specific annotations. +==== + +=== Documentation + +The `Documentation` annotation has the following contents, where the `info` and `revisions` annotations are described in <>, and the `figures` annotation is described in <>: +[source,modelica] +---- +record Documentation + /*literal*/ constant String info = "" "Description of the class"; + /*literal*/ constant String revisions = "" "Revision history"; + Figure[:] figures; "Simulation result figures"; + /*literal*/ constant String[:] styleSheets "Style sheets for documentation"; +end Documentation; +---- + +The `styleSheets` may also be given as a single string, see <>. + +How the tool interprets the information in `Documentation` is unspecified. + +==== Class Description and Revision History + +Inside the `Documentation` annotation, the `info` annotation gives a textual description of the class, and the `revisions` annotation gives a revision history. + +[NOTE] +==== +The `revisions` documentation may be omitted in printed documentation. +==== + +If the string starts with the tag `` or ``, the entire string is HTML encoded (assumed to end with `` or ``, but to be rendered as HTML even if the end-tag is missing). +Otherwise, the entire string is rendered as is. +HTML encoded content may contain links. +Modelica URIs may be used to refer to external resources (see <>), as well as to refer to Modelica classes, e.g., +[source,modelica] +---- +MultiBody.Tutorial +---- + +Together with scheme `Modelica` the (URI) fragment specifiers `#diagram`, `#info`, `#text`, `#icon` may be used to reference different layers. +User-defined fragment specifiers (anchors) may also be used, and they may be renamed when generating HTML (in particular to avoid collisions). +Example: +[source,modelica] +---- +Revolute +---- + +===== Style Sheets + +Inside the `Documentation` annotation, each element of the `styleSheets` annotation array specifies a cascading style sheet. +The style sheets are used when displaying the `info` and `revisions` annotations found in the `Documentation` annotations of the package. +The `styleSheets` annotation is only considered for top-level packages, and applies to the entire package. +The style sheets will be cascaded in the given order. +Specifying just a string for `styleSheets` has the same meaning as specifying a singleton array containing the string. + +[NOTE] +==== +It is recommended to use `class` and `id` selectors with a `NameOfLibrary-` prefix to avoid collisions when the content is included in a larger context. +==== + +The style sheet rules should not use type or universal selectors, due to possible interference with tool-specific styling. + +Vendors should use a `NameOfVendor-` prefix to style vendor generated HTML content surrounding the user provided documentation. +If tools want to give users (of that tool) the possibility to override the tool-specific CSS they can document that. +The prefix is used to avoid this happening by accident. + +==== Figures + +Inside the `Documentation` annotation, each element of the `figures` annotation array has the following content: +[source,modelica] +---- +record Figure + /*literal*/ constant String title = "" "Title meant for display"; + /*literal*/ constant String identifier = "" "Identifier meant for programmatic access"; + /*literal*/ constant String group = "" "Name of figure group"; + /*literal*/ constant Boolean preferred = false "Automatically display figure after simulation"; + Plot[:] plots "Plots"; + /*literal*/ constant String caption = "" "Figure caption"; +end Figure; +---- + +A `Figure` is a graphical container that can contain several `plots` described by `Plot` annotations: +[source,modelica] +---- +record Plot + /*literal*/ constant String title "Title meant for display"; + /*literal*/ constant String identifier = "" "Identifier meant for programmatic access"; + Curve[:] curves "Plot curves"; + /*literal*/ constant Axis x "X axis properties"; + /*literal*/ constant Axis y "Y axis properties"; +end Plot; +---- + +A `Plot` can contain several `curves`, see <>, that all share a common `x` and `y` axis with properties described in <>. + +Both `Figure` and `Plot` can have an optional title. +When the `Figure` `title` is the empty string (the default), the tool must produce a non-empty title based on the figure content. +On the other hand, the `Plot` `title` has a tool-dependent default, but the default may be the empty string. +When the `Plot` `title` is the empty string, no title should be shown. +The plot title is not to be confused with the plot _label_ which is never empty, see below. +Variable replacements, as described in <>, can be used in the `title` of `Figure` and `Plot`. + +The `identifier` in `Figure` and `Plot` is a `String` identifier, and is intended to identify the `Figure` and `Plot` for programmatic access. +The `figures` annotation is inherited in the sense that each class has a collection of figures comprised by the contents of the `figures` annotation in the class itself, as well as the `figures` annotations from any base classes. +A `Figure` must be uniquely identified by its `identifier` and a class having it in its collection. +This means that a `Figure` `identifier` must be unique among all `Figure` annotations within the same `figures` annotation as well as among all `figures` annotations from inherited classes. +A `Plot` `identifier` on the other hand is only required to be unique among the `plots` in the the same `Figure` annotation. +If an `identifier` is an empty string it cannot be used for programmatic access and is exempt from the uniqueness requirements. + +[NOTE] +==== +For `Figure`, this makes it possible to reference the plot from a tool-specific scripting environment. +For `Plot`, this makes it possible to reference the plot in the figure caption, which becomes useful when the `Figure` contains more than one `Plot`. +==== + +// henrikt-ma 2020-06: Once there is Modelica URI support for referring to a figure in the collection of a class, it will be easier to explain +// the following statement. +Even though a `Figure` annotation can be shared through inheritance between classes in a class hierarchy, note that each simulated class provides its own data to be displayed in the figure. + +Every `Plot` has an automatically generated _label_ which is required to be shown as soon as at least one `Plot` in the `Figure` has an `identifier`. +A tool is free to choose both labeling scheme (such as _a_, _b_, ..., or _i_, _ii_, ...), placement in the plot, and styling in the plot itself as well as in other contexts. + +When a `Figure` defines a non-empty `group`, it is used to organize figures similar to how `group` is used in the `Dialog` annotation (see <>). +However, leaving `group` at the default of an empty string does not mean that a group will be created automatically, but that the figure resides outside of any group. +The `group` is both the key used for grouping, and the name of the group for display purposes. + +The `preferred` attribute of `Figure` indicates whether the figure should be given preference when automatically determining which figures to show, and a class may define any number of `preferred` figures. +For example, a tool might choose to automatically show all preferred figures when the class is simulated. + +The `caption` attribute of `Figure` can use the restricted form of text markup described in <> as well as the variable replacements described in <>. + +===== Axis Properties + +Properties may be defined for each `Plot` axis (see also annotation index: Axis): + +[source,modelica] +---- +record Axis + /*literal*/ constant Real min "Axis lower bound, in 'unit'"; + /*literal*/ constant Real max "Axis upper bound, in 'unit'"; + /*literal*/ constant String unit = "" "Unit of axis tick labels"; + /*literal*/ constant String label "Axis label"; + /*literal*/ constant AxisScale scale = Linear() "Mapping between axis values and position on axis" +end Axis; +---- + +When an axis bound is not provided, the tool computes one automatically. + +A non-empty `unit` shall match `unit-expression` in <>. +An empty `unit` means that the axis is unitless, and each expression plotted against it may use its own unit determined by the tool. +The tool is responsible for conveying the information about choice of unit for the different variables, for instance by attaching this information to curve legends. + +The Modelica tool is responsible for showing that values at the axis tick marks are expressed in `unit`, so the axis `label` shall not contain this information. + +[NOTE] +When `unit` is empty, and axis bounds are to be determined automatically, a natural choice of unit could be the variable's `displayUnit`. +When axis bounds are specified by the user, on the other hand, a tool may choose a unit for the variable such that the range of the variable values (expressed in the chosen unit) fit nicely with the range of the unitless axis. + +If a tool does not recognize the `unit`, it is recommended to issue a warning and treat the `unit` as if it was empty, as well as ignore any setting for `min` and `max`. + +When `label` is not provided, the tool produces a default label. +Providing the empty string as `label` means that no label should be shown. +Variable replacements, as described in <>, can be used in `label`. +The Modelica tool is responsible for showing the unit used for values at the axis tick marks, so the axis `label` shall not contain the unit. + +The type of `scale` is defined as an empty partial record: + +[source,modelica] +---- +partial record AxisScale +end AxisScale; +---- + +The standardized annotations extending from `AxisScale` are `Linear` and `Log`, but it is also allowed to use a vendor-specific annotation. + +Use `Linear` for a linear mapping between axis values and position on axis: + +[source,modelica] +---- +record Linear + extends AxisScale; +end Linear; +---- + +Use `Log` for a logarithmic mapping between axis values and position on axis: + +[source,modelica] +---- +record Log + extends AxisScale; + /*literal*/ constant Integer base(min = 2) = 10; +end Log; +---- + +The `base` of a `Log` scale determines preferred positions of major axis ticks. +It is not required that the presentation of axis tick labels reflect the `base` setting. +For example, when `base` is 10, major axis ticks should preferrably be placed at integer powers of 10, and natural alternatives that a tool may use for major axis tick labels could look like 0.001 or 10^-3^. +Under some circumstances, such as when the axis range does not span even a single order of magnitude, a tool may disregard the preference in order to get useful axis ticks. + +[example] +==== +A _symmetric log_ axis scale is sometimes used for axes spanning across several orders of magnitude of both positive and negative values. +Details vary, but the mapping from value to linear position along axis is some variation of latexmath:[y \mapsto \operatorname{sign}(y)\, \operatorname{log}(1 + \frac{\left| y \right|}{10^{\alpha}})]. +A tool may implement this as a vendor-specific axis scale: + +[source,modelica] +---- +Axis( + min = -1e5, max = 1e5, + scale = __NameOfVendor_symlog(1), +) +---- +==== + +===== Plot Curves + +The actual data to plot is specified in the `curves` annotationindex:Curve of a `Plot`: +[source,modelica] +---- +record Curve + expression x = time "X coordinate values"; + expression y "Y coordinate values"; + /*literal*/ constant String legend "Legend"; + /*literal*/ constant Integer zOrder = 0 "Drawing order control"; +end Curve; +---- + +The mandatory `x` and `y` expressions are restricted to be result references in the form of `result-reference` in the grammar (see <>), referring to a scalar variable (or a derivative thereof) or `time`. +It is an error if `x` or `y` does not designate a scalar result. +If `x` or `y` is a derivative, `der(v, n)`, then n must not exceed the maximum amount of differentiation applied to `v` in the model. +A diagnostic is recommended in case the simulation result is missing a trajectory for a valid result reference. + +[NOTE] +==== +While the syntax for referring to a second order derivative is `der(v, 2)`, the appearance is left for tools to decide. +For example, a tool might choose to present this as `der(der(v))`. +==== + +When the `unit` of an `Axis` is non-empty, it is an error if the unit of the corresponding `x` or `y` expression (i.e., a variable's `unit`, or second for `time`) is incompatible with the axis unit. + +When `legend` is not provided, the tool produces a default based on `x` and/or `y`. +Providing the empty string as `legend` means that the curve shall be omitted from the plot legend. +Variable replacements, as described in <>, can be used in `legend`. +The order of presentation within the plot legend corresponds to order of appearance in the `curves` of a `Plot`. + +The `zOrder` gives control over drawing order, with higher values corresponding to closer to front. +Ties are resolved using order of appearance in the `curves` of a `Plot`, with later appearance corresponding to closer to front. + +===== Escape Sequences + +In an attribute inside a figure where the variable replacements of <> or the text markup of <> can be used, the following use of text markup escape sequences applies. +These escape sequences are applied after the application of other markup, and are not applied at all inside some of the other markup, see details for the respective markup. + +The percent character `%` shall be encoded `%%`. +The following are all the recognized escape sequences: + +[cols="a,a,a",options=autowidth] +|=== +|Sequence |Encoded character |Comment + +|`%%` |`%` |Only way to encode character. +|`%]` |`]` |Prevents termination of markup delimited by `[...]`. +|=== + +[NOTE] +==== +With the percent character being encoded as `%%`, the behavior of `%` appearing in any other way than the escape sequences above, for variable replacement (see <>), or for the text markup (see <>) is undefined, and thus possible to define in the future without breaking backward compatibility. +==== + +===== Vendor-Specific Markup + +Vendor-specific markup takes the form `__nameOfVendor~1~(data~1~)...__nameOfVendor~n~(data~n~)`, where _n_ ≥ 1. +It is only allowed as part of constructs described in <> and <>, where it will be denoted by vendorSpecificMarkup. +The nameOfVendor consists of only digits and letters, and shall only convey the name of the vendor defining the meaning of the associated data. +Text markup escape sequences don't apply inside the data, implying that it cannot contain the closing parenthesis, `)`. +A tool which does not understand any of the vendor-specific meanings will always be able to safely ignore all vendor-specific markup. + +===== Variable Replacements + +In the places listed in <> where text for display is defined, the final value of a result variable can be embedded by referring to the variable as `%{inertia1.w}`. +This is similar to the `Text` graphical primitive in <>. + +[[attributes-with-variable-replacements]] +.Attributes that can use variable replacements. +[cols="a,a",options=autowidth] +|=== +|Attribute |Annotation + +|`title` |`Figure` and `Plot` +|`caption` |`Figure` +|`legend` |`Curve` +|`label` |`Axis` +|=== + +In `%{variable}`, text markup escape sequences don't apply inside the variable, which has the form of `result-reference`. +This means that a complete `result-reference` shall be scanned before looking for the terminating closing brace. + +[example] +==== +Example: The variable replacement `%{'%%'}` references the variable `'%%'`, not the variable `'%'`. +==== + +[example] +==== +Example: The variable replacement `%{foo . '}bar{'}` makes a valid reference to the variable `foo.'}bar{'`. +==== + +Note that expansion to the final value means that expansion is not restricted to parameters and constants, so that values to be shown in a caption can be determined during simulation. + +[NOTE] +==== +By design, neither `%class` nor `%name` is supported in this context, as this information is expected to already be easily accessible (when applicable) in tool-specific ways. +(Titles making use of `%class` or `%name` would then only lead to ugly duplication of this information.) +==== + +Vendor-specific markup can be added to a variable replacement in the form `%vendorSpecificMarkup{variable}`. +The vendor-specific markup must not fundamentally alter the meaning of the variable replacement, in order to ensure that a tool can safely ignore all vendor-specific markup and still obtain a result that fits the current context. + +[example] +==== +Example: One application of vendor-specific markup for variable replacement is to prototype a feature that can later be turned into standardized format control. +For example, the replaced variable may have an automatically inferred unit, but no `displayUnit`-attribute. +The tool vendor AVendor could then describe a selection of display unit with `%__AVendor(?displayUnit=mm){integrator1.y}`. +Later, if this would become supported by standard variable replacement, it might take the form of something like `%{integrator1.y?displayUnit=mm}` instead. +==== + +===== Text Markup in Captions + +In addition to variable replacements, a very restricted form of text markup is used for the `caption`. +Note that the text markup escape sequences described in <> generally apply inside `caption`, with one exception given below for links. + +Links take the form `%[text](link)`, where the `[text]` part is optional, and text markup escape sequences don't apply inside the link. +The link can be in either of the following forms, where the interpretation is given by the first matching form: + +* A `variable:id`, where id is a component reference in the form of `result-reference` in the grammar, such as `inertia1.w`. + +* A `plot:id`, where id is the identifier of a `Plot` in the current `Figure`. + +* A URI. Well established schemes such as `+https://github.com/modelica+` or `modelica:/Modelica`, as well as lesser known schemes may be used. + (A tool that has no special recognition of a scheme can try sending the URI to the operating system for interpretation.) + +When `[text]` is omitted, a Modelica tool is free to derive a default based on the link. + +[NOTE] +==== +Note that for the character `]` to appear in text, it needs to be encoded as the escape sequence `%]`, or it would be interpreted as the terminating delimiter of the `[text]`. + +Similarly, the closing parenthesis `)` must be handled with care in link in order to not be interpreted as the terminating delimiter of the `(link)`. + +* For a `variable:`, no special treatment is needed, as the component reference syntax of the id allows parentheses to appear without risk of misinterpretation inside a quoted identifier. + For example, `%(variable:'try)me!')` has a parenthesis in `'try)me!'` that must not be mistaken for the end of the `(link)`. + +* For a `plot:`, there is currently no way to reference a plot with `)` in its `identifier`. + +* For a URI, a closing parenthesis must be URL encoded in order to not be interpreted as the end of the `(link)`. + For example, the URL in `+%(http://example.org/(tryme))+` is just `+http://example.org/(tryme+`, and the entire link is followed by a stray closing parenthesis. + To make it work, one has to use URL encoding: `+%(http://example.org/%28tryme%29)+` (using URL encoding of the opening parenthesis just for symmetry, and note that the `%` of the percent-encoded sequences are not subject to text markup escape sequences). +==== + +The styling of the link text, as well as the link action, is left for each Modelica tool to decide. + +[NOTE] +==== +For example, `%(variable:inertia1.w)` could be displayed as the text `inertia1.w` formatted with upright monospaced font, and have a pop-up menu attached with menu items for plotting the variable, setting its start value, or investigating the equation system from which it is solved. +On the other hand, `%[angular velocity](variable:inertia1.w)` could be formatted in the same style as the surrounding text, except some non-intrusive visual clue about it being linked. +==== + +[NOTE] +==== +Note that link is currently not allowed to be a URI reference, i.e., a URI or a relative reference such as `#foo`. +This is due to to the current inability to define a base URI referencing the current figure. +Once this becomes possible, the URI form of link may be changed into a URI reference. +==== + +Vendor-specific markup can be added to a link in the form `%[text]vendorSpecificMarkup(link)`. +The vendor-specific markup must not fundamentally alter the appearance of the link, in order to ensure that a tool can safely ignore all vendor-specific markup and still obtain a result that fits the current context. + +[example] +==== +Example: The HTML `` tag has several attributes with potential application to links, such as `target`. +This attribute serves a natural purpose if the display of figures is integrated with the display of documentation. +The tool vendor AVendor could map the HTML feature to Modelica in the form `%__AVendor(?target=_blank)(modelica:/Modelica#info)`. +==== + +A sequence of one or more newlines (encoded either literally or using the `\n` escape sequence) means a paragraph break. +(A line break within a paragraph is not supported, and any paragraph break before the first paragraph or after the last paragraph has no impact.) + +Vendor-specific markup for alternative content takes the form `%vendorSpecificMarkup[text]`. +The vendor-specific markup must not fundamentally alter the appearance of the text, in order to ensure that a tool can safely ignore all vendor-specific markup and still obtain a result that fits the current context. + +[example] +==== +Example: One application of vendor-specific alternative content is to prototype a feature that can later be turned into standardized markup. +For example, say that the tool AVendor wants to generalize the variable replacements such that the duration of a simulation can be substituted into a caption. +During the development, this could be represented as the vendor-specific markup `%__AVendor(?duration)[10 s]`, if the simulation has a duration of 10 seconds at the time of writing the caption. +When AVendor renders this, it ignores the text `10 s` and just displays the actual duration instead. +Later, if this would become supported by standard markup, it might take the form of something like `%{experiment:duration}` instead (note that `experiment:duration` is not in the form of a component reference, avoiding conflict with current use of variable replacements). + +In a similar way, vendor-specific alternative content can be used to prototype a link for future inclusion in the link markup (either by extending the meaning of Modelica URIs, or by introducing another pseudo-scheme similar to `variable:`). +This is an example where the vendor-specific markup could make use of the text (for link text) together with the vendor-specific data (describing the actual link). +==== + +=== Symbolic Processing +:id: annotations-for-symbolic-processing + +The annotation listed below, in addition to annotations described in <> through <>, can influence the symbolic processing. + +[cols="a,a,a",options=autowidth] +|=== +|Annotation |Description |Details + +|`Evaluate` |Use parameter value for symbolic processing |<> +|=== + +[[annotation:Evaluate,Annotation Evaluate]] +Annotation Evaluate:: ++ +[source,modelica] +---- +/*literal*/ constant Boolean Evaluate; +---- ++ +The annotation `Evaluate` can occur in the component declaration, its type declaration, or a base class of the type-declaration. +In the case of multiple conflicting annotations it is handled similarly to modifiers (e.g., an `Evaluate` annotation on the component declaration takes precedence). +In the case of hierarchical components it is applied to all components, overriding any `Evaluate`-setting for specific components. +The annotation `Evaluate` is only allowed for parameters and constants. ++ +Setting `Evaluate = true` for an evaluable parameter, means that it must be an evaluated parameter (i.e., its value must be determined during translation, similar to a constant). +For a non-evaluable parameter, it has no impact and it is recommended to issue a warning in most cases. +The exception for recommending this warning is when the parameter is non-evaluable due to dependency on a parameter with `Evaluate = false`, as this could be a sign of intentional overriding of `Evaluate = true`, see example below. +For both evaluable parameters and constants, the model developer further proposes to utilize the value for symbolic processing. +A constant can never be changed after translation, and it is normal for its value to be used for symbolic processing even without `Evaluate = true`. ++ +For a parameter, `Evaluate = false` ensures that the parameter is a non-evaluable parameter according to <> (meaning it is not allowed to be used where an evaluable expression (<>) is expected). +For both parameters and constants -- even when the value can be determined during translation -- the model developer further proposes to not utilize the value for symbolic processing. ++ +If the annotation is missing for a parameter or constant the evaluation of the component is tool-dependent. ++ +[NOTE] +-- +`Evaluate = true` is for example used for axis of rotation parameters in the `Modelica.Mechanics.MultiBody` library in order to improve the efficiency of the generated code. + +Conversely, a possible use of `Evaluate = false` is to ensure that a parameter can be changed after translation, even when a tool might be tempted to evaluate it to improve the efficiency of the generated code. +-- ++ +[example] +==== +Example: When a parameter has `Evaluate = true` for optimization reasons (not because it needs to be evaluable), it is possible to prevent the value from being determined during translation without modifying the original model: + +[source,modelica] +---- +model M_evaluable + /* Here, 'b' is evaluable, and will be evaluated. */ + parameter Boolean b = false annotation(Evaluate = true); + Real x(start = 1.0, fixed = true); +equation + if b then /* No need for b to be evaluable. */ + der(x) = x; + else + der(x) = -x; + end if; +end M_evaluable; + +model M_non_evaluable + /* Here, 'bn' is non-evaluable, which in turn will cause 'b' to be + * non-evaluable, thereby preventing it from being determined during + * translation. + */ + extends M_evaluable(b = bn); + parameter Boolean bn = false annotation(Evaluate = false); +end M_non_evaluable; +---- +==== + +=== Simulations +:id: annotations-for-simulations + +The annotations listed below define how models can be checked, translated, and simulated. + +[cols="a,a,a",options=autowidth] +|=== +|Annotation |Description |Details + +|`experiment` |Simulation experiment settings |<> +|`HideResult` |Don't show component's simulation result |<> +|`TestCase` |Information for model used as test case |<> +|=== + +[[annotation:experiment,Annotation experiment]] +Annotation experiment:: ++ +[source,modelica] +---- +record experiment + /*literal*/ constant Real StartTime(unit = "s") = 0; + /*literal*/ constant Real StopTime(unit = "s"); + /*literal*/ constant Real Interval(unit = "s"); + /*literal*/ constant Real Tolerance(unit = "1"); +end experiment; +---- ++ +The `experiment` annotation defines the start time (`StartTime`) in [s], the stop time (`StopTime`) in [s], the suitable time resolution for the result grid (`Interval`) in [s], and the relative integration tolerance (`Tolerance`) for simulation experiments to be carried out with the model or block at hand. +When `Interval` or `Tolerance` is not provided, the tool is responsible for applying appropriate defaults. ++ +The experiment options are inherited, and the derived class may override individual inherited options. ++ +[NOTE] +The inheritance makes it useful to have an `experiment` annotation also in partial models, e.g., a template for a number of similar test cases. ++ +If `StopTime` is set in a non-partial model, it is required to be a simulation model. +Tools can allow users to override these settings without modifying the model. + +[[annotation:HideResult,Annotation HideResult]] +Annotation HideResult:: ++ +[source,modelica] +---- +/*literal*/ constant Boolean HideResult; +---- ++ +`HideResult = true` defines that the model developer proposes to not show the simulation results of the corresponding component. ++ +`HideResult = false` defines that the developer proposes to show the corresponding component. ++ +[NOTE] +-- +For example, a tool is not expected to provide means to plot a variable with `HideResult = true`. +If a variable is declared in a protected section, a tool might not include it in a simulation result. +By setting `HideResult = false`, the modeler would like to have the variable in the simulation result, even if in the protected section. + +`HideResult` is for example used in the connectors of the `Modelica.StateGraph` library to not show variables to the modeler that are of no interest to him and would confuse him. +-- + +[[annotation:TestCase,Annotation TestCase]] +Annotation TestCase:: ++ +[source,modelica] +---- +record TestCase + /*literal*/ constant Boolean shouldPass; +end TestCase; +---- ++ +If `shouldPass` is `false` it indicates that the translation or the simulation of the model should fail. +If a tools checks a package where classes have `shouldPass = false` they should not generate errors, and checking may even be skipped. +On the other hand, models with `shouldPass = false` may be useful for creation of negative tests in tool-specific ways. +Similarly as a class with `obsolete` annotation, a class with `TestCase` annotation (regardless of the value of `shouldPass`) shall not be used in other models, unless those models also have a `TestCase` annotation. ++ +If the `TestCase` annotation is missing it is a normal model -- there are thus no restrictions on the use of the model, and the model shall not contain errors. ++ +[NOTE] +The intent of the test-case can be included in the documentation of the class. +This annotation can both be used for models intended as test-cases for implementations, and for models explaining detectable errors. + +=== Usage Restrictions +:id: usage-restrictions + +The annotations listed below are used to restrict the ways in which classes and instances of classes may be used. + +[cols="a,a,a",options=autowidth] +|=== +|Annotation |Description |Details + +|`singleInstance` |Allow at most one instance |<> +|`mustBeConnected` |Connector must be connected at least once |<> +|`mayOnlyConnectOnce` |Connector may at most be connected once |<> +|=== + +[[annotation:singleInstance,Annotation singleInstance]] +Annotation singleInstance:: ++ +[source,modelica] +---- +/*literal*/ constant Boolean singleInstance; +---- ++ +Allowed for class annotations. +Only has effect when `true`, meaning that there should only be one component instance of the class, and it should be in the same scope as the class is defined. +The intent is to remove the class when the component is removed and to prevent duplication of the component. ++ +[NOTE] +This is useful for the local classes of state machines. + +[[annotation:mustBeConnected,Annotation mustBeConnected]] +Annotation mustBeConnected:: ++ +[source,modelica] +---- +/*literal*/ constant String mustBeConnected; +---- ++ +Allowed for connector component declarations. +If specified, it makes it an error if the connector does not appear as an inside connector in any connect-equation (for a conditional connector this check is only active if the connector is enabled). +The string value must be non-empty and provide the reason why it must be connected. +For an array of connectors it applies separately to each element. ++ +[NOTE] +This annotation is intended for non-causal connectors, see <>. +It is particularly suited for stream connectors, see <>. + ++ +[example] +==== +Example: This can be used for some optional connectors that should be connected when conditionally enabled. + +[source,modelica] +---- +partial model PartialWithSupport + Flange_b flange; + parameter Boolean useSupport; + Support support if useSupport + annotation( + mustBeConnected = "Support connector should be connected if activated."); +end PartialWithSupport; +---- + +The protected components and connections needed to internally handle the support-connector is omitted. +==== + +[[annotation:mayOnlyConnectOnce,Annotation mayOnlyConnectOnce]] +Annotation mayOnlyConnectOnce:: ++ +[source,modelica] +---- +/*literal*/ constant String mayOnlyConnectOnce; +---- ++ +Allowed for connector component declarations. +If specified, it makes it an error if the connector is connected as an inside connector in a connect-equation and thus appears in a connection set if: ++ +* For non-stream connectors the connection set has more than two elements. ++ +* For stream connectors (see <>), the connection set has more than two elements whose flow variable may be negative (based on evaluation of the `min`-attribute). ++ +For an array of connectors it applies separately to each element. +The string value must be non-empty and provide the reason why it may only be connected once. ++ +[NOTE] +-- +This annotation is intended for non-causal connectors, see <>. +The connection handling operates on connection sets, and thus this restriction should also operate on those sets. +The set handling avoids the case where only one of two equivalent models generate diagnostics. +The stream connector part is primarily intended to exclude sensor-variables, see <>, but also excludes non-reversible outgoing flows. +-- ++ +[example] +==== +Example: This can be used for components that implement mixing of fluids where it is not desired to combine that with the normal stream-connector mixing. + +[source,modelica] +---- +partial model MultiPort + parameter Integer n = 0 annotation(Dialog(connectorSizing = true)); + FluidPort_a port_a(redeclare package Medium = Medium); + FluidPorts_b ports_b[n](redeclare each package Medium = Medium) + annotation (mayOnlyConnectOnce = "Should only connect once per element!"); +end MultiPort; +---- +==== + +=== Graphical Objects +:id: annotations-for-graphical-objects + +A graphical representation of a class consists of two abstraction layers, icon layer and diagram layer showing graphical objects, component icons, connectors and connection lines. +The icon representation typically visualizes the component by hiding hierarchical details. +The hierarchical decomposition is described in the diagram layer showing icons of subcomponents and connections between these. + +Graphical annotations described in this chapter ties into the Modelica grammar as follows. + +[source,grammar] +---- +graphical-annotations : + annotation "(" [ layer-annotations ] ")" + +layer-annotations : + ( icon-layer | diagram-layer ) [ "," layer-annotations ] +---- + +Layer descriptions (start of syntactic description): + +[source,grammar] +---- +icon-layer : + "Icon" "(" [ coordsys-specification "," ] graphics ")" + +diagram-layer : + "Diagram" "(" [ coordsys-specification "," ] graphics ")" +---- + +[example] +==== +Example: + +[source,modelica] +---- +annotation( + Icon(coordinateSystem(extent = {{-100, -100}, {100, 100}}), + graphics = {Rectangle(extent = {{-100, -100}, {100, 100}}), + Text(extent = {{-100, -100}, {100, 100}}, + textString = "Icon")})); +---- +==== + +The graphics is specified as an ordered sequence of graphical primitives described below. +Base class contents are drawn behind the graphical primitives of the current class, with base classes ordered from back to front according to the order of the `extends`-clauses, and graphical primitives according to order of appearance in the annotation. + +[NOTE] +Note that the ordered sequence is syntactically a valid Modelica annotation, although there is no mechanism for defining an array of heterogeneous objects in Modelica. + +These `Icon`, `Diagram`, and `Documentation` annotations are only allowed directly in classes (e.g., not on components or connections). +The allowed annotations for a short class definition is the union of the allowed annotations in classes and on `extends`-clauses. + +==== Common Definitions +:id: common-definitions + +The following common definitions are used to define graphical annotations in the later sections. + +[source,modelica] +---- +type DrawingUnit = Real(final unit="mm"); +type Point = DrawingUnit[2] "{x, y}"; +type Extent = Point[2] "Defines a rectangular area {{x1, y1}, {x2, y2}}"; +---- + +The interpretation of `unit` is with respect to printer output in natural size (not zoomed). + +All graphical entities have a visible attribute which indicates if the entity should be shown. + +[source,modelica] +---- +partial record GraphicItem + Boolean visible = true; + Point origin = {0, 0}; + Real rotation(quantity="angle", unit="deg")=0; +end GraphicItem; +---- + +The `origin` attribute specifies the origin of the graphical item in the coordinate system of the layer in which it is defined. +The origin is used to define the geometric information of the item and for all transformations applied to the item. +All geometric information is given relative the `origin` attribute, which by default is `{0, 0}`. + +The `rotation` attribute specifies the rotation of the graphical item counter-clockwise around the point defined by the `origin` attribute. + +===== Coordinate Systems +:id: coordinate-systems + +Each of the layers has its own coordinate system. +A coordinate system is defined by the coordinates of two points, the left (x1) lower (y1) corner and the right (x2) upper (y2) corner, where the coordinates of the first point shall be less than the coordinates of the second point. + +The attribute `preserveAspectRatio` specifies a hint for the shape of components of the class, but does not actually influence the rendering of the component. +If `preserveAspectRatio` is true, changing the extent of components should preserve the current aspect ratio of the coordinate system of the class. + +The attribute `initialScale` specifies the default component size as `initialScale` times the size of the coordinate system of the class. +An application may use a different default value of `initialScale`. + +The attribute `grid` specifies the spacing between grid points which can be used by tools for alignment of points in the coordinate system, e.g., "snap-to-grid". +Its use and default value is tool-dependent. + +[source,modelica] +---- +record CoordinateSystem + /*literal*/ constant Extent extent; + /*literal*/ constant Boolean preserveAspectRatio = true; + /*literal*/ constant Real initialScale = 0.1; + /*literal*/ constant DrawingUnit grid[2]; +end CoordinateSystem; +---- + +[example] +==== +Example: A coordinate system for an icon could for example be defined as: + +[source,modelica] +---- +CoordinateSystem(extent = {{-10, -10}, {10, 10}}); +---- + +i.e., a coordinate system with width 20 units and height 20 units. +==== + +The coordinate systems for the icon and diagram layers are by default defined as follows; where the array of `GraphicItem` represents an ordered list of graphical primitives. + +[source,modelica] +---- +record Icon "Representation of the icon layer" + CoordinateSystem coordinateSystem(extent = {{-100, -100}, {100, 100}}); + GraphicItem[:] graphics; +end Icon; + +record Diagram "Representation of the diagram layer" + CoordinateSystem coordinateSystem(extent = {{-100, -100}, {100, 100}}); + GraphicItem[:] graphics; +end Diagram; +---- + +The coordinate system attributes (`extent` and `preserveAspectRatio`) of a class are separately defined by the following priority: + +. The coordinate system annotation given in the class (if specified). +. The coordinate systems of the first base class where the extent on the `extends`-clause specifies a null-region (if any). +Note that null-region is the default for base classes, see <>. +. The default coordinate system `CoordinateSystem(preserveAspectRatio=true, extent = {{-100, -100}, {100, 100}})`. + +===== Graphical Properties +:id: graphical-properties + +Properties of graphical objects and connection lines are described using the following attribute types. + +[source,modelica] +---- +type Color = Integer[3](min = 0, max = 255) "RGB representation"; +constant Color Black = zeros(3); +type LinePattern = enumeration(None, Solid, Dash, Dot, DashDot, DashDotDot); +type FillPattern = enumeration(None, Solid, Horizontal, Vertical, + Cross, Forward, Backward, CrossDiag, + HorizontalCylinder, VerticalCylinder, Sphere); +type BorderPattern = enumeration(None, Raised, Sunken, Engraved); +type Smooth = enumeration(None, Bezier); +type EllipseClosure = enumeration(None, Chord, Radial, Automatic); +---- + +The `LinePattern` attribute `Solid` indicates a normal line, `None` an invisible line, and the other attributes various forms of dashed/dotted lines. + +The `FillPattern` attributes `Horizontal`, `Vertical`, `Cross`, `Forward`, `Backward` and `CrossDiag` specify fill patterns drawn with the line color over the fill color. + +The attributes `HorizontalCylinder`, `VerticalCylinder` and `Sphere` specify gradients that represent a horizontal cylinder, a vertical cylinder and a sphere, respectively. +The gradient goes from line color to fill color. + +The border pattern attributes `Raised`, `Sunken` and `Engraved` represent frames which are rendered in a tool-dependent way --- inside the extent of the filled shape. + +.Line with `smooth = Bezier`. The four line points P₁, ..., P₄ result in two quadratic splines and two straight line segments. +image::media/bezierpoints.svg[] + +The `smooth` attribute specifies that a line can be drawn as straight line segments (`None`) or using a spline (`Bezier`), where the line's points specify control points of a quadratic Bezier curve, see figure above. + +For lines with only two points, the `smooth` attribute has no effect. + +For lines with three or more points (P₁, P₂, ..., Pₙ), the middle point of each line segment (P₁₂, P₂₃, ..., P₍ₙ₋₁₎ₙ) becomes the starting point and ending points of each quadratic Bezier curve. +For each quadratic Bezier curve, the common point of the two line segment becomes the control point. +For instance, point P₂ becomes the control point for the Bezier curve starting at P₁₂ and ending at P₂₃. +A straight line is drawn between the starting point of the line and the starting point of the first quadratic Bezier curve, as well as between the ending point of the line and the ending point of the last quadratic Bezier curve. + +In the illustration above, the square points (P₁, P₂, P₃, and P₄) represent the points that define the line, and the circle points (P₁₂, P₂₃, and P₃₄) are the calculated middle points of each line segment. +Points P₁₂, P₂, and P₂₃ define the first quadratic Bezier curve, and the points P₂₃, P₃, and P₃₄ define the second quadratic Bezier curve. +Finally a straight line is drawn between points P₁ and P₁₂ as well as between P₃₄ and P₄. + +The values of the `EllipseClosure` enumeration specify if and how the endpoints of an elliptical arc are to be joined (see <>). + +[source,modelica] +---- +type Arrow = enumeration(None, Open, Filled, Half); +type TextStyle = enumeration(Bold, Italic, UnderLine); +type TextAlignment = enumeration(Left, Center, Right); +---- + +Filled shapes have the following attributes for the border and interior. + +[source,modelica] +---- +record FilledShape "Style attributes for filled shapes" + Color lineColor = Black "Color of border line"; + Color fillColor = Black "Interior fill color"; + LinePattern pattern = LinePattern.Solid "Border line pattern"; + FillPattern fillPattern = FillPattern.None "Interior fill pattern"; + DrawingUnit lineThickness = 0.25 "Line thickness"; +end FilledShape; +---- + +The extent/points of the filled shape describe the theoretical zero-thickness filled shape, and the actual rendered border is then half inside and half outside the extent. + +==== Component Instance +:id: component-instance + +A component instance can be placed within a diagram or icon layer. +It has an annotation with a `Placement` modifier to describe the placement. +Placements are defined in terms of coordinate system transformations: + +[source,modelica] +---- +record Transformation + Extent extent; + Real rotation(quantity = "angle", unit = "deg") = 0; + Point origin = {0, 0}; +end Transformation; +---- + +The attributes are applied in the order `extent`, `rotation`, `origin`, as follows: + +. The `extent` of the component icon is mapped to the `extent` rectangle (possibly shifting, scaling, and flipping contents). +. The `rotation` specifies counter-clockwise rotation around the origin (that is `{0, 0}`, not the `origin` attribute). +. The `origin` specifies a shift (moving `{0, 0}` to `origin`). + +[source,modelica] +---- +record Placement + Boolean visible = true; + Transformation transformation "Placement in the diagram layer"; + Boolean iconVisible "Visible in icon layer; for public connector"; + Transformation iconTransformation + "Placement in the icon layer; for public connector"; +end Placement; +---- + +A component with `visible = false` shall not be shown. +If no `iconTransformation` is given the `transformation` is also used for placement in the icon layer. +If no `iconVisible` is given for a public connector the `visible` is also used for visibility in the icon layer. + +[NOTE] +A connector can be shown in both an icon layer and a diagram layer of a class. +Since the coordinate systems typically are different, placement information needs to be given using two different coordinate systems. +More flexibility than just using scaling and translation is needed since the abstraction views might need different visual placement of the connectors. +The attribute `transformation` gives the placement in the diagram layer and `iconTransformation` gives the placement in the icon layer. +When a connector is shown in a diagram layer, its diagram layer is shown to facilitate opening up a hierarchical connector to allow connections to its internal subconnectors. + +For connectors, the icon layer is used to represent a connector when it is shown in the icon layer of the enclosing model. +The diagram layer of the connector is used to represent it when shown in the diagram layer of the enclosing model. +Protected connectors are only shown in the diagram layer. +Public connectors are shown in both the diagram layer and the icon layer. +Sub-connectors in a hierarchical connector are only shown when they can be connected to. +Non-connector components are only shown in the diagram layer. + +===== Default Outline for Missing Graphics + +If the icon of a component placed in a diagram layer does not contain any graphical primitives (including +inherited ones, and regardless of `visible`-attributes; but excluding connectors), tools shall show a tooldependent rudimentary outline of the component’s transformed `extent`. + +[NOTE] +The reason for making the tool-dependent outline rudimentary is to encourage the model developer to provide a proper icon. That `visible`-attributes are not regarded makes it possible to obtain an icon which only shows connectors by adding a dummy primitive with `visible = false`. + +==== Extends-Clause +:id: extends-clause + +Each `extends`-clause (and short class definition, as stated in <>) may have layer specific annotations which describe the rendering of the base class' icon and diagram layers in the derived class. + +[source,modelica] +---- +record IconMap + /*literal*/ constant Extent extent = {{0, 0}, {0, 0}}; + /*literal*/ constant Boolean primitivesVisible = true; +end IconMap; + +record DiagramMap + /*literal*/ constant Extent extent = {{0, 0}, {0, 0}}; + /*literal*/ constant Boolean primitivesVisible = true; +end DiagramMap; +---- + +All graphical objects are by default inherited from a base class. +If the `primitivesVisible` attribute is false, components and connections are visible but graphical primitives are not. + +* If the `extent` is `{{0, 0}, {0, 0}}` (the default), the base class contents is mapped to the same coordinates in the derived class, and the coordinate system (including `preserveAspectRatio`) can be inherited as described in <>. +* For any other `extent`, the base class coordinate system is mapped to this region, with the exception that `preserveAspectRatio = true` in the base class requires that the mapping shall preserve the aspect ratio. +The base class coordinate system (and `preserveAspectRatio`) is not inherited. + +[NOTE] +-- +A zero area `extent` other than `{{0, 0}, {0, 0}}` will result in none of the base class contents being visible. +By affecting components and connections as well as graphical primitives, this is different from setting `primitivesVisible = false`. + +Reversed corners of the `extent` will result in mirrored (rotated if reversed in both direction) base class contents. +-- + +[example] +==== +Example: + +[source,modelica] +---- +model A + extends B annotation( + IconMap(extent = {{-100, -100}, {100, 100}}, primitivesVisible = false), + DiagramMap(extent = {{-50, -50}, {0, 0}}, primitivesVisible = true) + ); +end A; + +model B + extends C annotation(DiagramMap(primitivesVisible = false)); + ... +end B; +---- + +In this example the diagram of `A` contains the graphical primitives from `A` and `B` (but not from `C` since they were hidden in `B`) -- the ones from `B` are rescaled, and the icon of `A` contains the graphical primitives from `A` (but neither from `B` nor from `C`). +==== + +==== Connections +:id: connections1 + +A connection is specified with an annotation containing a `Line` primitive and optionally a `Text` primitive, as specified below. + +[example] +==== +Example: + +[source,modelica] +---- +connect(a.x, b.x) + annotation(Line(points = {{-25, 30}, {10, 30}, {10, -20}, {40, -20}})); +---- +==== + +The optional `Text` primitive defines a text that will be written on the connection line. +It has the following definition (it is not equal to the `Text` primitive as part of graphics -- the differences are marked after _Note_ in the description-strings): + +[source,modelica] +---- +record Text + extends GraphicItem; + Extent extent; + String string "Note: different name"; + Real fontSize = 0 "unit pt"; + String fontName; + TextStyle textStyle[:]; + Color textColor = Black; + TextAlignment horizontalAlignment = + if index < 0 then TextAlignment.Right else TextAligment.Left "Note: different default"; + Integer index "Note: new"; +end Text; +---- + +The `index` is one of the points of Line (numbered 1, 2, 3, ... where negative numbers count from the end, thus -1 indicate the last one). +The `string` may use the special symbols `"%first"` and `"%second"` to indicate the connectors in the `connect`-equation. + +The `extent` and `rotation` are relative to the `origin` (default `{0, 0}`) and the `origin` is relative to the point on the `Line`. + +The `textColor` attribute defines the color of the text. +The text is drawn with transparent background and no border around the text (and without outline). +The default value for `horizontalAlignment` is deprecated. +Having a zero size for the `extent` is deprecated and is handled as if upper part is moved up an appropriate amount. + +[example] +==== +Example: + +[source,modelica] +---- +connect(controlBus.axisControlBus1, axis1.axisControlBus) + annotation( + Text(string = "%first", index = -1, extent = [-6, 3; -6, 7]), + Line(points = {{41, 30}, {50, 30}, {50, 50}, {58, 50}}) + ); +---- + +Draws a connection line and adds the text _axisControlBus1_ ending at (-6, 3) + (58, 50) and 4 vertical units of space for the text. +Using a height of zero, such as `extent = [-6, 3; -6, 3]` is deprecated, but gives similar result. +==== + +==== Graphical Primitives +:id: graphical-primitives + +This section describes the graphical primitives that can be used to define the graphical objects in an annotation. + +===== Line +:id: line + +A line is specified as follows: + +[source,modelica] +---- +record Line + extends GraphicItem; + Point points[:]; + Color color = Black; + LinePattern pattern = LinePattern.Solid; + DrawingUnit thickness = 0.25; + Arrow arrow[2] = {Arrow.None, Arrow.None} "{start arrow, end arrow}"; + DrawingUnit arrowSize = 3; + Smooth smooth = Smooth.None "Spline"; +end Line; +---- + +Note that the `Line` primitive is also used to specify the graphical representation of a connection. + +For arrows: + +* The arrow is drawn with an aspect ratio of 1/3 for each arrow half, i.e., if the arrow-head is 3 mm long an arrow with `Half` will extend 1 mm from the mid-line and with `Open` or `Filled` extend 1 mm to each side, in total making the base 2 mm wide. +* The `arrowSize` gives the width of the arrow (including the imagined other half for `Half`) so that `thickness = 10` and `arrowSize = 10` will touch at the outer parts. +* All arrow variants overlap for overlapping lines. +* The lines for the `Open` and `Half` variants are drawn with `thickness`. + +===== Polygon +:id: polygon + +A polygon is specified as follows: + +[source,modelica] +---- +record Polygon + extends GraphicItem; + extends FilledShape; + Point points[:]; + Smooth smooth = Smooth.None "Spline outline"; +end Polygon; +---- + +The polygon is automatically closed, if the first and the last points are not identical. + +===== Rectangle +:id: rectangle + +A rectangle is specified as follows: + +[source,modelica] +---- +record Rectangle + extends GraphicItem; + extends FilledShape; + BorderPattern borderPattern = BorderPattern.None; + Extent extent; + DrawingUnit radius = 0 "Corner radius"; +end Rectangle; +---- + +The `extent` attribute specifies the bounding box of the rectangle. +If the `radius` attribute is specified, the rectangle is drawn with rounded corners of the given radius. + +===== Ellipse +:id: ellipse + +An ellipse is specified as follows: + +[source,modelica] +---- +record Ellipse + extends GraphicItem; + extends FilledShape; + Extent extent; + Real startAngle(quantity = "angle", unit = "deg") = 0; + Real endAngle(quantity = "angle", unit = "deg") = 360; + EllipseClosure closure = EllipseClosure.Automatic; +end Ellipse; +---- + +The `extent` attribute specifies the bounding box of the ellipse. + +Partial ellipses can be drawn using the `startAngle` and `endAngle` attributes. +These specify the endpoints of the arc prior to the stretch and rotate operations. +The arc is drawn counter-clockwise from `startAngle` to `endAngle`, where `startAngle` and `endAngle` are defined counter-clockwise from 3 o'clock (the positive x-axis). + +The `closure` attribute specifies whether the endpoints specified by `startAngle` and `endAngle` are to be joined by lines to the center of the extent (`closure = EllipseClosure.Radial`), joined by a single straight line between the end points (`closure = EllipseClosure.Chord`), or left unconnected (`closure = EllipseClosure.None`). +In the latter case, the ellipse is treated as an open curve instead of a closed shape, and the `fillPattern` and `fillColor` are not applied (if present, they are ignored). + +The effect of `EllipseClosure.Automatic` is that of `EllipseClosure.Chord` when both `startAngle` is 0 and `endAngle` is 360, and that of `EllipseClosure.Radial` otherwise. + +[NOTE] +==== +The default for a closed ellipse is not `EllipseClosure.None`, since that would result in `fillColor` and `fillPattern` being ignored, making it impossible to draw a filled ellipse. +`EllipseClosure.Chord` is equivalent in this case, since the chord will be of zero length. +==== + +===== Text +:id: text + +A text string is specified as follows: + +[source,modelica] +---- +record Text + extends GraphicItem; + Extent extent; + String textString; + Real fontSize = 0 "unit pt"; + String fontName; + TextStyle textStyle[:]; + Color textColor = Black; + TextAlignment horizontalAlignment = TextAlignment.Center; +end Text; +---- + +The `textColor` attribute defines the color of the text. +The text is drawn with transparent background and no border around the text (and without outline). + +There are a number of common macros that can be used in the text, and they should be replaced when displaying the text as follows (in order such that the earliest ones have precedence, and using the longest sequence of identifier characters -- alphanumeric and underscore): + +* `%%` replaced by `%` +* `%name` replaced by the name of the component (i.e., the identifier for it in the enclosing class). +* `%class` replaced by the name of the class (only the last part of the hierarchical name). +* `%_par_` and `%{_par_}` replaced by the value of the parameter `par`. +If the value is numeric, tools shall display the value with `displayUnit`, formatted according to the BIPM specification. +E.g., for ++ +[source,modelica] +---- +parameter Real t(unit = "s", displayUnit = "ms") = 0.1 +---- ++ +tools shall display `_100 ms_`. +The intent is that the text is easily readable, thus if `par` is of an enumeration type, replace `%_par_` by the item name, not by the full name. ++ +[example] +==== +Example: If `par = "Modelica.Blocks.Types.Enumeration.Periodic"`, then `%_par_` should be displayed as _Periodic_. +==== ++ +When quoted identifiers (e.g., `'quoted ident'` or `'}'`) or composite names (i.e., not simple identifiers) are involved, the form `%{_par_}` must be used. +Here, `_par_` is a general `component-reference`, and `%{a.p}` gives the value of the parameter `p` in the component `a`. +The macro can be directly followed by a letter. +Thus `%{w}x%{h}` gives the value of `w` directly followed by `_x_` and the value of `h`, while `%wxh` gives the value of the parameter `wxh`. +If the parameter does not exist it is an error. + +The style attribute `fontSize` specifies the font size. +If the `fontSize` attribute is 0 the text is scaled to fit its extent. +Otherwise, the size specifies the absolute size. +The text is vertically centered in the extent. + +If the `extent` specifies a box with zero width and positive height the height is used as height for the text (unless `fontSize` attribute is non-zero -- which specifies the absolute size), and the text is not truncated (the `horizontalAlignment` is still used in this case). + +[NOTE] +A zero-width `extent` is convenient for handling texts where the width is unknown. + +If the string `fontName` is empty, the tool may choose a font. +The font names `"serif"`, `"sans-serif"`, and `"monospace"` shall be recognized. +If possible the correct font should be used -- otherwise a reasonable match, or treat as if `fontName` was empty. + +The style attribute `textStyle` specifies variations of the font. + +===== Bitmap +:id: bitmap + +A bitmap image is specified as follows: + +[source,modelica] +---- +record Bitmap + extends GraphicItem; + Extent extent; + String fileName "Name of bitmap file"; + String imageSource "Base64 representation of bitmap"; +end Bitmap; +---- + +The `Bitmap` primitive renders a graphical bitmap image. +The data of the image can either be stored on an external file or in the annotation itself. +The image is scaled to fit the extent. +Given an extent `{{x₁, y₁}, {x₂, y₂}}`, `x₂ < x₁` defines horizontal flipping and `y₂ < y₁` defines vertical flipping around the center of the object. + +The graphical operations are applied in the order: scaling, flipping and rotation. + +When the attribute `fileName` is specified, the string refers to an external file containing image data. +The mapping from the string to the file is specified for some URIs in <>. +The supported file formats include `PNG`, `BMP`, `JPEG`, and `SVG`. + +When the attribute `imageSource` is specified, the string contains the image data, and the image format is determined based on the contents. +The image is represented as a Base64 encoding of the image file format (see RFC 4648, http://tools.ietf.org/html/rfc4648). + +The image is uniformly scaled (preserving the aspect ratio) so it exactly fits within the extent (touching the extent along one axis). +The center of the image is positioned at the center of the extent. + +==== Variable Graphics and Schematic Animation +:id: variable-graphics-and-schematic-animation + +Any value in graphical annotations can be dependent on evaluable parameters except when restricted otherwise in their respective definitions, (for example with `/* literal */ constant`). + +`DynamicSelect` has the syntax of a function call with two arguments, where the first argument specifies the value of the static state and the second argument the value of the dynamic state. +The first argument follows the same rules as when not using `DynamicSelect`. +The second argument may contain references to variables of a higher variability to enable displaying dynamic behavior of a simulation. + +[example] +==== +Example: The level of a tank could be animated by a rectangle expanding in vertical direction and its color depending on a variable overflow: + +[source,modelica] +---- +annotation(Icon(graphics = { + Rectangle( + extent = + DynamicSelect({{0, 0}, {20, 20}}, + {{0, 0}, {20, level}}), + fillColor = + DynamicSelect({0, 0, 255}, + if overflow then {255, 0, 0} else {0, 0, 255}) + )})); +---- +==== + +==== User Input +:id: user-input + +It is possible to interactively modify variables during a simulation. +The variables may either be parameters, discrete-time variables or states. +New numeric values can be given, a mouse click can change a `Boolean` variable or a mouse movement can change a `Real` variable. +Input fields may be associated with a `GraphicItem` or a component as an array named `interaction`. +The `interaction` array may occur as an attribute of a graphic primitive, an attribute of a component annotation or as an attribute of the layer annotation of a class. + +===== Mouse Input +:id: mouse-input + +A `Boolean` variable can be changed when the cursor is held over a graphical item or component and the selection button is pressed if the interaction annotation contains `OnMouseDownSetBoolean`: + +[source,modelica] +---- +record OnMouseDownSetBoolean + Boolean variable "Name of variable to change when mouse button pressed"; + Boolean value "Assigned value"; +end OnMouseDownSetBoolean; +---- + +[example] +==== +Example: A button can be represented by a rectangle changing color depending on a `Boolean` variable `on` and toggles the variable when the rectangle is clicked on: + +[source,modelica] +---- +annotation(Icon( + graphics = { + Rectangle(extent = [0, 0; 20, 20], + fillColor = if on then {255, 0, 0} else {0, 0, 255})}, + interaction = {OnMouseDownSetBoolean(on, not on)})); +---- +==== + +In a similar way, a variable can be changed when the mouse button is _released_: + +[source,modelica] +---- +record OnMouseUpSetBoolean + Boolean variable "Name of variable to change when mouse button released"; + Boolean value "Assigned value"; +end OnMouseUpSetBoolean; +---- + +Note that several interaction objects can be associated with the same graphical item or component. + +[example] +==== +Example: + +[source,modelica] +---- +interaction = {OnMouseDownSetBoolean(on, true), + OnMouseUpSetBoolean(on, false)} +---- +==== + +The `OnMouseMoveXSetReal` interaction object sets the variable to the position of the cursor in X direction in the local coordinate system mapped to the interval defined by the `minValue` and `maxValue` attributes. + +[source,modelica] +---- +record OnMouseMoveXSetReal + Real xVariable "Name of variable to change when cursor moved in x direction"; + Real minValue; + Real maxValue; +end OnMouseMoveXSetReal; +---- + +The `OnMouseMoveYSetReal` interaction object works in a corresponding way as the `OnMouseMoveXSetReal` object but in the Y direction. + +[source,modelica] +---- +record OnMouseMoveYSetReal + Real yVariable "Name of variable to change when cursor moved in y direction"; + Real minValue; + Real maxValue; +end OnMouseMoveYSetReal; +---- + +===== Edit Input +:id: edit-input + +The `OnMouseDownEditInteger` interaction object presents an input field when the graphical item or component is clicked on. +The field shows the actual value of the variable and allows changing the value. +If a too small or too large value according to the `min` and `max` parameter values of the variable is given, the input is rejected. + +[source,modelica] +---- +record OnMouseDownEditInteger + Integer variable "Name of variable to change"; +end OnMouseDownEditInteger; +---- + +The `OnMouseDownEditReal` interaction object presents an input field when the graphical item or component is clicked on. +The field shows the actual value of the variable and allows changing the value. +If a too small or too large value according to the `min` and `max` parameter values of the variable is given, the input is rejected. + +[source,modelica] +---- +record OnMouseDownEditReal + Real variable "Name of variable to change"; +end OnMouseDownEditReal; +---- + +The `OnMouseDownEditString` interaction object presents an input field when the graphical item or component is clicked on. +The field shows the actual value of the variable and allows changing the value. + +[source,modelica] +---- +record OnMouseDownEditString + String variable "Name of variable to change"; +end OnMouseDownEditString; +---- + +=== Graphical User Interface + +The annotations listed below define properties for use in graphical user interfaces. + +[cols="a,a,a",options=autowidth] +|=== +|Annotation |Description |Details + +|`preferredView` |Default view when opening class |<> +|`DocumentationClass` |Purpose of class is documentation |<> +|`defaultComponentName` |Default name for new components |<> +|`defaultComponentPrefixes` |Default type prefixes for new components |<> +|`missingInnerMessage` |Message for unresolved `outer` |<> +|`absoluteValue` |Quantity is absolute |<> +|`defaultConnectionStructurallyInconsistent` |Suppress certain verification errors |<> +|`obsolete` |Message when using obsolete class |<> +|`unassignedMessage` |Hint for unmatched variable |<> +|`Dialog` |Setup for modifications |<> +|=== + +[[annotation:preferredView,Annotation preferredView]] +Annotation preferredView:: ++ +[source,modelica] +---- +/*literal*/ constant String preferredView; +---- ++ +The `preferredView` annotation defines the default view when selecting the class. +The value `"info"` means class documentation ("information"), `"diagram"` means diagram view, `"icon"` means icon view, and `"text"` means Modelica source code ("text"). +If not specified the default view is tool-specific. + +[[annotation:DocumentationClass,Annotation DocumentationClass]] +Annotation DocumentationClass:: ++ +[source,modelica] +---- +/*literal*/ constant Boolean DocumentationClass; +---- ++ +Only allowed as class annotation on any kind of class and only having effect when `true`, meaning that this class and all classes within it are treated as having the annotation `preferredView = "info"`. +If the annotation `preferredView` is explicitly set for a class, it has precedence over a `DocumentationClass` annotation. ++ +[NOTE] +A tool may display such classes in special ways. +For example, the description texts of the classes might be displayed instead of the class names, and if no icon is defined, a special information default icon may be displayed in the package browser. + +[[annotation:defaultComponentName,Annotation defaultComponentName]] +Annotation defaultComponentName:: ++ +[source,modelica] +---- +/*literal*/ constant String defaultComponentName; +---- ++ +The class annotation `defaultComponentName` gives the recommended component name to use when creating a component of the class. +If the default name cannot be used (e.g., since it is already in use), another name based on `defaultComponentName` shall be derived automatically, except as described under `defaultComponentPrefixes`. +It is an error if the string is not a valid identifier. +When automatically deriving a name, any trailing '`1`' in the `defaultComponentName` shall be disregarded. +If not specified, the names of new components are tool-specific. + +[[annotation:defaultComponentPrefixes,Annotation defaultComponentPrefixes]] +Annotation defaultComponentPrefixes:: ++ +[source,modelica] +---- +/*literal*/ constant String defaultComponentPrefixes; +---- ++ +The class annotation `defaultComponentPrefixes` gives a whitespace separated list of recommended type prefixes to include in the `type-prefix` part of a `component-clause1` generated when creating a component of the class: ++ +[source,grammar] +---- +type-prefix type-specifier component-declaration +---- ++ +The following prefixes may be included in the `defaultComponentPrefixes` string: `inner`, `outer`, `replaceable`, `constant`, `parameter`, `discrete`. +The default is an empty string. ++ +[NOTE] +By using `defaultComponentPrefixes` in combination with `defaultComponentName`, it becomes easy for users to create `inner` components matching the `outer` declarations; see also example below. +If the type prefixes contain `inner` or `outer` and the default name cannot be used (e.g., since it is already in use) it is recommended to give a diagnostic. + +[[annotation:missingInnerMessage,Annotation missingInnerMessage]] +Annotation missingInnerMessage:: ++ +[source,modelica] +---- +/*literal*/ constant String missingInnerMessage; +---- ++ +Only has an effect if specified, and the string must then be non-empty. +When specified and an `outer` component of the class does not have a corresponding `inner` component, the string message may be used as part of a diagnostic message (together with appropriate context), see <>. +The default is a tool-specific diagnostic message. ++ +[example] +==== +Example: + +[source,modelica] +---- +model World + ... + annotation(defaultComponentName = "world", + defaultComponentPrefixes = "inner replaceable", + missingInnerMessage = "The World object is missing"); +end World; +---- +==== ++ +When an instance of model `World` is dragged in to the diagram layer, the following declaration is generated: ++ +[source,modelica] +---- +inner replaceable World world; +---- + +[[annotation:absoluteValue,Annotation absoluteValue]] +Annotation absoluteValue:: ++ +[source,modelica] +---- +/*literal*/ constant Boolean absoluteValue; +---- ++ +Allowed for simple types and components of a simple types. +If `false`, then the component defines a relative quantity, and if `true` an absolute quantity. +When converting between units (e.g., in plots and where parameters are edited), the unit offset must be ignored for relative quantities. +The annotation is inherited in the sense that when `absoluteValue` is defined for a simple type, it also applies derived classes. +When `absoluteValue` is defined for a simple type, it also applies to components declared with the type. ++ +When `absoluteValue` of a component is not determined by an annotation (possibly through inheritance), the `absoluteValue` status may be inferred by the tool. +If the `absoluteValue` of a component is neither determined by annotation nor inference, unit conversions that would differ depending on `absoluteValue` cannot be performed. ++ +[NOTE] +For most quantities there are no units with offset, and the annotation is not needed. +For a component where unit conversions involving offsets could be of interest (mainly temperatures), ensuring that `absoluteValue` is determined by an annotation (typically by means of using a type where it has been specified) may reduce impact of quality-of-implementation in tool ability to infer `absoluteValue`. +Example applications of this annotation can be found among the type definitions in the `Modelica.Units` package of the Modelica Standard Library, such as `TemperatureDifference`. + +[[annotation:defaultConnectionStructurallyInconsistent,Annotation defaultConnectionStructurallyInconsistent]] +Annotation defaultConnectionStructurallyInconsistent:: ++ +[source,modelica] +---- +/*literal*/ constant Boolean defaultConnectionStructurallyInconsistent; +---- ++ +Allowed for model and block class definitions. +Only has an effect if `true`, when it is stated that a _default connection_ will result in a structurally inconsistent model or blockfootnote:[For the precise definition of _structurally inconsistent_, see Pantelides1988ConsistentInitialization.]. +Here, the _default connection_ is constructed by instantiating the respective `model` or `block` and for every input `u` providing an equation `0 = f(u)`, and for every (potential, flow) pair of the form `(v, i)`, providing an equation of the form `0 = f(v, i)`. ++ +[NOTE] +It is useful to check all models/blocks of a Modelica package in a simple way. +One check is to default connect every model/block and to check whether the resulting class is structurally consistent (which is a stronger requirement than being balanced). +It is rarely needed; but is for example used in `Modelica.Blocks.Math.InverseBlockConstraints`, in order to prevent a wrong error message. +Additionally, when a user defined model is structurally inconsistent, a tool should try to pinpoint in which class the error is present. +This annotation avoids then to show a wrong error message. + +[[annotation:obsolete,Annotation obsolete]] +Annotation obsolete:: ++ +[source,modelica] +---- +/*literal*/ constant String obsolete; +---- ++ +Allowed for class annotations. +Only has an effect if specified, and the string must then be non-empty. +It indicates that the class ideally should not be used anymore and gives a message indicating the recommended action. +This annotation is not inherited, the assumption is that if a class uses an obsolete class (as a base class or as the class of one of the components) that shall be updated -- ideally without impacting users of the class. +If that is not possible the current class can have also have an `obsolete` annotation. + +[[annotation:unassignedMessage,Annotation unassignedMessage]] +Annotation unassignedMessage:: ++ +[source,modelica] +---- +/*literal*/ constant String unassignedMessage; +---- ++ +Allowed for component declarations. +Only has an effect if specified, and the string must then be non-empty. +When the variable to which this annotation is attached in the declaration cannot be computed due to the structure of the equations, the string can be used as a diagnostic message. ++ +[NOTE] +When using BLT partitioning, this means if a variable `a` or one of its aliases `b = a` or `b = -a` cannot be assigned, the message is displayed. +This annotation is used to provide library specific error messages. ++ +[example] +==== +Example: + +[source,modelica] +---- +connector Frame "Frame of a mechanical system" + ... + flow Modelica.Units.SI.Force f[3] + annotation(unassignedMessage = + "All Forces cannot be uniquely calculated. The reason could be that the + mechanism contains a planar loop or that joints constrain the same motion. + For planar loops, use in one revolute joint per loop the option + PlanarCutJoint=true in the Advanced menu. + "); +end Frame; +---- +==== + +[[annotation:Dialog,Annotation Dialog]] +Annotation Dialog:: ++ +[source,modelica] +---- +record Dialog + /*literal*/ constant String tab = "General"; + /*literal*/ constant String group = ""; + /*evaluable*/ parameter Boolean enable = true; + /*literal*/ constant Boolean showStartAttribute; + /*literal*/ constant Boolean colorSelector = false; + /*literal*/ constant Selector loadSelector; + /*literal*/ constant Selector saveSelector; + /*literal*/ constant Selector directorySelector; + /*literal*/ constant String groupImage = ""; + /*literal*/ constant Boolean connectorSizing = false; +end Dialog; + +record Selector + /*literal*/ constant String filter = ""; + /*literal*/ constant String caption = ""; +end Selector; +---- ++ +Allowed for component declarations and short replaceable class definitions. +For a short replaceable class definition only the fields `tab`, `group`, `enable` and `groupImage` are allowed. ++ +In the organization of a tool's user interface, the `tab` shall correspond to a major divisioning of "tabs", and `group` correspond to sub-divisioning of "groups" within each tab. +An empty `group` (the default) means tool-specific choice of group. +The order of components (and class definitions) within each group and the order of the groups and tabs are according to the declaration order, where inherited elements are added at the place of the extends. ++ +A component shall have at most one of `showStartAttribute=true`, `colorSelector=true`, `loadSelector`, `saveSelector`, `directorySelector`, or `connectorSizing=true`. ++ +Modifiable parameters (except for `connectorSizing = true`), non-connector inputs, and short replaceable class definitions should normally be shown in the dialog even without this annotation. ++ +[example] +==== +Example: When `group` is empty, a tool may place parameters in the group "Parameters", and place variables with `showStartAttribute = true` in the group "Start Attributes". +==== ++ +If `enable = false`, the input field may be disabled and no input can be given. ++ +If `showStartAttribute = true` the dialog should allow the user to set the `start`- and `fixed`-attributes for the variable instead of the value of the variable. ++ +[NOTE] +The `showStartAttribute = true` is primarily intended for non-parameter values and avoids introducing a separate parameter for the `start`-attribute of the variable. + ++ +If a non-parameter declaration has a modifier for the `start`-attribute and does not have `showStartAttribute = false`, the `start`- and `fixed`-attributes may also be shown. ++ +If `colorSelector = true`, it suggests the use of a color selector to pick an RGB color as a vector of three values in the range 0..255 (the color selector should be useable both for vectors of `Integer` and `Real`). ++ +The presence of `loadSelector` or `saveSelector` specifying `Selector` suggests the use of a file dialog to select a file. +Setting `filter` will in the dialog only show files that fulfill the given pattern. +Setting `text1 (*.ext1);;text2 (*.ext2)` will only show files with file extensions `ext1` or `ext2` with the corresponding description texts `text1` and `text2`, respectively. +`caption` is a caption for display in the file dialog. +`loadSelector` is used to select an existing file for reading, whereas `saveSelector` is used to define a file for writing. ++ +The presence of `directorySelector` specifying `Selector` suggests the use of a dialog to select an existing directory. +The selected directory does not need to exist at the time of opening the dialog; it is allowed to let the dialog be used to create directory before selecting it. +The `filter` may not be used. +The `caption` is a caption for display in the file dialog. ++ +The `groupImage` references an image using an URI (see <>), and the image is intended to be shown together with the entire group (only one image per group is supported). +Disabling the input field will not disable the image. +The background of the `groupImage` and any image used in HTML-documentation is recommended to be transparent (intended to be a light color) or white. ++ +The `connectorSizing` is described separately in <>. +A dialog annotation only containing `showStartAttribute = false` and/or `connectorSizing = true` does not indicate that the variable shall be shown. ++ +[example] +==== +Example: + +[source,modelica] +---- +model DialogDemo + parameter Boolean b = true "Boolean parameter"; + parameter Modelica.Units.SI.Length length "Real parameter with unit"; + parameter Real r1 "Real parameter in Group 1" + annotation(Dialog(group = "Group 1")); + parameter Real r2 "Disabled Real parameter in Group 1" + annotation(Dialog(group = "Group 1", enable = not b)); + parameter Real r3 "Real parameter in Tab 1" + annotation(Dialog(tab = "Tab 1")); + parameter Real r4 "Real parameter in Tab 1 and Group 2" + annotation(Dialog(tab = "Tab 1", group = "Group 2")); + ... +end DialogDemo; +---- + +When clicking on an instance of model `DialogDemo`, a dialog is shown that may have the following layout (other layouts are also possible, this is vendor specific). + +image::media/disabledparameter.png[] + +image::media/tabparameter.png[] +==== + +==== Connector Sizing +:id: connector-sizing + +This section describes the `connectorSizing` annotation inside a `Dialog` annotation. +The value of `connectorSizing` must be a literal `false` or `true`. +If `connectorSizing = false`, this annotation has no effect. +If `connectorSizing = true`, the corresponding variable must be declared with the `parameter` prefix, must be a subtype of a scalar `Integer` and must have a literal default value of zero. + +[NOTE] +==== +The reason why `connectorSizing` must be given a literal value is that if the value is an expression, the `connectorSizing` functionality is conditional and this will then lead easily to wrong models. + +The default value of the variable must be zero since this annotation is designed for a parameter that is used as vector dimension, and the dimension of the vector should be zero when the component is dragged or redeclared. +Furthermore, when a tool does not support the `connectorSizing` annotation, dragging will still result in a correct model. +==== + +If `connectorSizing = true`, a tool may set the parameter value in a modifier automatically, if used as dimension size of a vector of connectors. +In that case the parameter should not be modified by the user, and a tool may choose to not display that parameter in the dialog or display it with disabled input field. + +[NOTE] +==== +The `connectorSizing` annotation is used in cases where connections to a vector of connectors shall be made and a new connection requires to resize the vector and to connect to the new index (unary connections). +The annotation allows a tool to perform these two actions in many cases automatically. +This is, e.g., very useful for state machines and for certain components of fluid libraries. +==== + +[NOTE] +==== +The following part is non-normative text and describes a useful way to handle the `connectorSizing` annotation in a tool (still a tool may use another strategy and/or may handle other cases than described below). +The recommended rules are clarified at hand of the following example which represents a connector and a model from the `Modelica.StateGraph` library (note that they may be modified or renamed in future versions): + +[source,modelica] +---- +connector Step_in // Only 1:1 connections are possible since input used + output Boolean occupied; + input Boolean set; +end Step_in; + +block Step + // nIn cannot be set through the dialog (but maybe shown) + parameter Integer nIn=0 annotation(Dialog(connectorSizing=true)); + Step_in inPorts[nIn]; + ... +end Step; +---- + +If the parameter is used as dimension size of a vector of connectors, it is automatically updated according to the following rules: + +. [[connectorSizing:addVector]] +If a new connection line is drawn between one outside and one inside vector of connectors both dimensioned with (`connectorSizing`) parameters, a connection between the two vectors is performed and the (`connectorSizing`) parameter is propagated from connector to component. +Other types of outside connections do not lead to an automatic update of a (`connectorSizing`) parameter. +_Example:_ Assume there is a connector `inPorts` and a component `step1`: ++ +[source,modelica] +---- +parameter Integer nIn=0 annotation(Dialog(connectorSizing=true)); +Step_in inPorts[nIn]; +Step step1(nIn=0); +---- ++ +Drawing a connection line between connectors `inPorts` and `step1.inPorts` results in: ++ +[source,modelica] +---- + parameter Integer nIn=0 annotation(Dialog(connectorSizing=true)); + Step_in inPorts[nIn]; + Step step1(nIn=nIn); // nIn=0 changed to nIn=nIn +equation + connect(inPorts, step1.inPorts); // new connect-equation +---- + +. [[connectorSizing:deleteVector]] +If a connection line is deleted between one outside and one inside vector of connectors both dimensioned with (`connectorSizing`) parameters, the `connect`-equation is removed and the (`connectorSizing`) parameter of the component is set to zero or the modifier is removed. +_Example:_ Assume the connection line in the resulting example in case 1 is removed. +This results in: ++ +[source,modelica] +---- +parameter Integer nIn=0 annotation(Dialog(connectorSizing=true)); +Step_in inPorts[nIn]; +Step step1; // modifier nIn=nIn is removed +---- + +. [[connectorSizing:addScalar]] +If a new connection line is drawn to an inside connector with `connectorSizing` and case 1 does not apply then, the parameter is incremented by one and the connection is performed for the new highest index. +_Example:_ Assume that 3 connections are present and a new connection is performed. +The result is: ++ +[source,modelica] +---- + Step step1(nIn=4); // index changed from nIn=3 to nIn=4 +equation + connect(..., step1.inPorts[4]); // new connect-equation +---- ++ +In some applications, like state machines, the vector index is used as a priority, e.g., to define which transition is firing if several transitions become active at the same time instant. +It is then not sufficient to only provide a mechanism to always connect to the last index. +Instead, some mechanism to select an index conveniently should be provided. + +. [[connectorSizing:deleteScalar]] +If a connection line is deleted to an inside connector with `connectorSizing` and case 2 does not apply then, then the (`connectorSizing`) parameter is decremented by one and all connections with index above the deleted connection index are also decremented by one. +Example: Assume there are 4 connections: ++ +[source,modelica] +---- + Step step1(nIn=4); +equation + connect(a1, step1.inPorts[1]); + connect(a2, step1.inPorts[2]); + connect(a3, step1.inPorts[3]); + connect(a4, step1.inPorts[4]); +---- ++ +and the connection from `a2` to `step1`. +`inPorts[2]` is deleted. +This results in ++ +[source,modelica] +---- + Step step1(nIn=3); +equation + connect(a1, step1.inPorts[1]); + connect(a3, step1.inPorts[2]); + connect(a4, step1.inPorts[3]); +---- + +These rules also apply if the connectors and/or components are defined in superclass. + +_Example:_ Assume that `step1` is defined in superclass `MyCompositeStep` with 3 connections, and a new connection is performed in a derived class. +The result is: + +[source,modelica] +---- + extends MyCompositeStep(step1(nIn=4)); // new modifier nIn=4 +equation + connect(..., step1.inPorts[4]); // new connect-equation +---- +==== + +[[annotations-for-version-handling]] +=== Versions + +A top-level package or model can specify the version of top-level classes it uses, its own version number, and if possible how to convert from previous versions. +This can be used by a tool to guarantee that consistent versions are used, and if possible to upgrade usage from an earlier version to a current one. + +==== Version Numbering +:id: version-numbering + +Version numbers are of the forms: + +* Main release versions: `""" UNSIGNED-INTEGER { "." UNSIGNED-INTEGER } """` + +Example: `"2.1"` + +* Pre-release versions: `""" UNSIGNED-INTEGER { "." UNSIGNED-INTEGER } " " {S-CHAR} """` + +Example: `"2.1 Beta 1"` + +* Un-ordered versions: `""" NON-DIGIT {S-CHAR} """` + +Example: `"Test 1"` + +The main release versions are ordered using the hierarchical numerical names, and follow the corresponding pre-release versions. +The pre-release versions of the same main release version are internally ordered alphabetically. + +==== Version Handling +:id: version-handling + +In a top-level class, the version number and the dependency to earlier versions of this class are defined using one or more of the following annotations: + +* `version = CURRENT-VERSION-NUMBER` +Defines the version number of the model or package. +All classes within this top-level class have this version number. +* `conversion(noneFromVersion = VERSION-NUMBER)` +Defines that user models using the `VERSION-NUMBER` can be upgraded to the `CURRENT-VERSION-NUMBER` of the current class without any changes. +* `conversion(from(version = Versions, [to=VERSION-NUMBER,] Convert))` +where _Versions_ is `VERSION-NUMBER` | `{VERSION-NUMBER, VERSION-NUMBER, ...}` and _Convert_ is `script="..."` | `change={conversionRule(), ..., conversionRule()}` +Defines that user models using the `VERSION-NUMBER` or any of the given `VERSION-NUMBER` can be upgraded to the given `VERSION-NUMBER` (if the to-tag is missing this is the `CURRENT-VERSION-NUMBER`) of the current class by applying the given conversion rules. +The script consists of an unordered sequence of `conversionRule();` (and optionally Modelica comments). +The `conversionRule` functions are defined in <>. ++ +[NOTE] +==== +The to-tag is added for clarity and optionally allows a tool to convert in multiple steps. +==== +* `uses(IDENT (version = VERSION-NUMBER [, versionBuild=INTEGER] [, dateModified=STRING] ) )` +Defines that classes within this top-level class use version `VERSION-NUMBER` of classes within the top-level class `IDENT`. + +The annotations `uses` and `conversion` may contain several different sub-entries. + +[example] +==== +Example: + +[source,modelica] +---- +package Modelica + ... + annotation( + version = "3.1", + conversion( + noneFromVersion = "3.1 Beta 1", + noneFromVersion = "3.1 Beta 2", + from(version = {"2.1", "2.2", "2.2.1"}, + script = "convertTo3.mos"), + from(version = "1.5", + script = "convertFromModelica1_5.mos"))); +end Modelica; + +model A + ... + annotation( + version = "1.0", + uses(Modelica(version = "1.5"))); +end A; + +model B + ... + annotation( + uses(Modelica(version = "3.1 Beta 1"))); +end B; +---- + +In this example the model `A` uses an older version of the Modelica library and can be upgraded using the given script, and model `B` uses an older version of the Modelica library but no changes are required when upgrading. +==== + +===== Conversion Rules +:id: conversion-rules + +There are a number of functions: `convertClass`, `convertClassIf`, `convertElement`, `convertModifiers`, `convertMessage` defined as follows. +The calls of these functions do not directly convert, instead they define conversion rules as below. +It is recommended, but not required, to terminate each such function call with a semi-colon. +The order between the function calls does not matter, instead the longer paths (in terms of number of hierarchical names) are used first as indicated below, and it is an error if there are any ambiguities. + +The conversion should generate correct Modelica models using the new version of the library corresponding to the old version. + +[NOTE] +==== +Whenever possible tools should preserve the original style of the model, e.g., use of imports. +Conversions should be applied in all places where named element are used in code, including Modelica URIs (for example, in `Documentation` annotations). +==== + +These functions can be called with literal strings or arrays of literal strings and vectorize according to <>. +The empty literal string is only allowed when constructing an empty array using `fill`. + +All of these convert-functions only use inheritance among user models, and not in the library that is used for the conversion -- thus conversions of base classes will require multiple conversion calls; this ensures that the conversion is independent of the new library structure. +The name of the class used as argument to `convertElement` and `convertModifiers` is similarly the old name of the class, i.e., the name before it is possibly converted by `convertClass`. + +[NOTE] +==== +Specifying conversions using the old name of a class allows the conversion to be done without access to the old version of the library (by suitable modifications of the lookup). +Another alternative is to use the old version of the library during the conversion. + +The invalid but previously used form `convertElement("OldClass", "OldName", "")` should be handled as `convertModifiers("OldClass", {"OldName"}, fill("", 0))` without any conversion applied to equations. +==== + +convertClass("OldClass", "NewClass"):: ++ +Convert class `OldClass` to `NewClass`. ++ +Match longer path first, so if converting both `A` to `C` and `A.B` to `D` then `A.F` is converted to `C.F` and `A.B.E` to `D.E`. +This is considered before `convertMessage` for the same `OldClass`. ++ +[example] +==== +Example: Consider the following as part of a conversion script: + +[source,modelica] +---- +convertClass("Modelica.SIunits", "Modelica.Units.SI"); +convertClass("Modelica.SIunits.Icons", "Modelica.Units.Icons"); +---- + +This ensures that for example `Modelica.SIunits.Length` is converted to `Modelica.Units.SI.Length` and `Modelica.SIunits.Icons` is converted to `Modelica.Units.Icons`. +==== + +convertClassIf("OldClass", "oldElement", "whenValue", "NewClass"):: ++ +Convert class `OldClass` to `NewClass` if the literal modifier for `oldElement` has the value `whenValue`, and also remove the modifier for `oldElement`. ++ +These are considered before `convertClass` and `convertMessage` for the same `OldClass`. ++ +The old element should be of a `Boolean`, `Integer`, `String`, or enumeration type and the match is based on the literal value of the modifier. +For string elements the value argument to `convertClassIf` shall be up-quoted, e.g., `"\"My String\""`, and for enumeration literals only the enumeration literal part of the old value matters, e.g., `red` for `"Colors.red"`. + +convertElement("OldClass", "OldName", "NewName"):: ++ +-- +In `OldClass`, convert element `OldName` to `NewName`. +Both `OldName` and `NewName` normally refer to components, but they may also refer to class-parameters, or hierarchical names. +For hierarchical names, the longest match is used first. + +For replaceable classes in packages (and replaceable classes in other classes) `convertElement` shall be used if the class is renamed within the package (or class), whereas `convertClass` shall only be used if the class is placed outside of the package (or class). + +[NOTE] +The latter case indicates a problem with overuse of replaceable classes in the previous design of the library. + +[example] +==== +Example: Consider the following as part of a conversion script: + +[source,modelica] +---- +convertElement({"Modelica.Mechanics.MultiBody.World", + "Modelica.Mechanics.MultiBody.World.gravityAcceleration"}, + "mue", "mu"); +---- + +This implies that + +[source,modelica] +---- +Modelica.Mechanics.MultiBody.World world(mue=2); +function f=Modelica.Mechanics.MultiBody.World.gravityAcceleration(mue=4); +---- + +is converted to: + +[source,modelica] +---- +Modelica.Mechanics.MultiBody.World world(mu=2); +function f=Modelica.Mechanics.MultiBody.World.gravityAcceleration(mu=4); +---- +==== +-- + +[[convertModifiers]] +convertModifiers:: ++ +-- +[source,modelica] +---- +convertModifiers("OldClass", + {"OldModifier1=default1", "OldModifier2=default2", ...}, + {"NewModifier1=...%OldModifier2%...", "NewModifier2=...", ...} + [, simplify=true]); +---- + +Normal case; if any modifier among `OldModifier` exist then replace all of them with the list of `NewModifiers`. +The `...%OldModifier2%...` indicate an expression that may involve the values of the old modifiers (tools are responsible for adding parentheses if needed). +The lists of old and new modifiers can have different lengths. +The defaults (if present) are used if there are multiple `OldModifier` and not all are set in the component instance. +The defaults are optional if there is at most one `OldModifier` element, and should otherwise be provided. + +If `simplify` is specified and true then perform obvious simplifications to clean up the new modifier; otherwise leave as is. + +The old and new modifiers shall be component references, except for the `cardinality` cases listed below. + +[NOTE] +Note: `simplify` is primarily intended for converting enumerations and emulated enumerations that naturally lead to large nested `if`-expressions. +The simplifications may also simplify parts of the original expression. + +If the modifiers contain literal string values they must be quoted. + +Behaviour in unusual cases: + +* If `NewModifier` list is empty then the modifier is just removed. + +* If `OldModifer` list is empty it is added for all uses of the class. + +* If `OldModifierᵢ` is `cardinality(a) = 0` the conversion will only be applied for a component comp if there are no inside connections to `comp.a`. +This can be combined with other modifiers that are handled in the usual way. + +* If `OldModifierᵢ` is `cardinality(a) = 1` the conversion will only be applied for a component comp if there are any inside connections to `comp.a`. + +The converted modifiers and existing modifiers are merged such that the existing modifiers take precedence over the result of `convertModifiers`. +A diagnostic is recommended if this merging removes some modifiers unless those modifiers are identical or it is the special case of an empty `OldModifier` list. + +[NOTE] +This can be used to handle the case where the default value was changed. + +Converting modifiers with cardinality is used to remove the deprecated operator `cardinality` from model libraries, and replace tests on cardinality in models by parameters explicitly enabling the different cases. +The case where the old class is used as a base class, and there exist outside connections to `a`, and there is `convertModifiers` involving the cardinality of `a` is not handled. + +[NOTE] +==== +Having a parameter for explicitly enabling the different cases means that instead of model `A` internally testing if its connector `B` is connected, there will be a parameter for enabling connector `B`, and the conversion ensures that each component of model `A` will have this parameter set accordingly. + +In case a parameter is simply renamed it is preferable to use `convertElement`, since that also handles, e.g., binding equations using the parameter. +==== + +[example] +==== +Example: The conversion + +[source,modelica] +---- +convertClass("Modelica.Thermal.FluidHeatFlow.Components.IsolatedPipe", + "Modelica.Thermal.FluidHeatFlow.Components.Pipe"); +convertModifiers({"Modelica.Thermal.FluidHeatFlow.Components.IsolatedPipe"}, + fill("", 0), {"useHeatPort=false"}); + +convertClass("Modelica.StateGraph.Temporary.NumericValue", + "Modelica.Blocks.Interaction.Show.RealValue"); +convertModifiers("Modelica.StateGraph.Temporary.NumericValue", + {"Value"}, {"number=%Value%"}); +convertModifiers("Modelica.StateGraph.Temporary.NumericValue", + {"hideConnector"}, {"use_numberPort=not %hideConnector%"}); + +convertModifiers("Modelica.Blocks.Math.LinearDependency", + {"y0=0", "k1=0", "k2=0"}, {"y0=%y0%", "k1=%y0%*%k1%", "k2=%y0%*%k2%"}, + true); + +convertClass("My.Library.BadPackage", + "My.Library.Package"); +convertElement("My.Library.BadPackage.PartialBase", + "bad", "correct"); +convertElement("My.Library.BadPackage.ActualClass", + "bad", "correct"); +---- + +converts + +[source,modelica] +---- +Modelica.Thermal.FluidHeatFlow.Components.IsolatedPipe pipe1; +Modelica.StateGraph.Temporary.NumericValue tempValue( + Value = 10, hideConnector = true); +Modelica.Blocks.Math.LinearDependency linearDep(y0 = 2, k2 = 1); +model A + import My.Library; + extends Library.BadPackage.ActualClass; +end A; +model B + extends A; + Boolean b = bad; +end B; +---- + +to + +[source,modelica] +---- +Modelica.Thermal.FluidHeatFlow.Components.Pipe pipe1(useHeatPort = false); +Modelica.Blocks.Interaction.Show.RealValue( + number = 10, use_numberPort = not true); +Modelica.Blocks.Math.LinearDependency linearDep(y0 = 2, k1 = 0, k2 = 2); +model A + import My.Library; + extends Library.Package.ActualClass; +end A; +model B + extends A; + Boolean b = correct; +end B; +---- + +The `convertElement` call for `ActualClass` is needed to avoid relying on base classes in the original library where `ActualClass` inherits from `PartialBase`. +However, the inheritance among the models to convert (in this case `B` inherits from `A`) should be handled. +Note that conversion works regardless of the import of `My.Library`. +==== +-- + +convertMessage("OldClass", "Failed Message"):: ++ +For any use of `OldClass` report that conversion could not be applied with the given message. ++ +[NOTE] +==== +This may be useful if there is no possibility to convert a specific class. +An alternative is to construct `ObsoleteLibraryA` for problematic cases, which may be more work but allows users to directly run the models after the conversion and later convert them. +==== + +convertMessage("OldClass", "Failed Message", "oldElement"):: ++ +For any use of `oldElement` in `OldClass` report that conversion could not be applied with the given message. ++ +[NOTE] +==== +This is useful if there is no possibility to convert a specific parameter (or other element), especially if it rarely modified. +If the parameter had no impact on the model it can be removed using `convertModifiers`, see <>. +==== + +==== Versions in the File System +:id: mapping-of-versions-to-file-system + +A top-level class, `IDENT`, with version `VERSION-NUMBER` can be stored in one of the following ways in a directory given in the `MODELICAPATH`: + +* The file `IDENT ".mo"` + +Example: `Modelica.mo` + +* The file `IDENT " " VERSION-NUMBER ".mo"` + +Example: `Modelica 2.1.mo` + +* The directory `IDENT` with the file `package.mo` directly inside it + +Example: `Modelica/package.mo` + +* The directory `IDENT " " VERSION-NUMBER` with the file `package.mo` directly inside it + +Example: `Modelica 2.1/package.mo` + +This allows a tool to access multiple versions of the same package. + +==== Version Date and Build Information +:id: version-date-and-build-information + +Besides version information, a top-level class can have additionally the following top-level annotations to specify associated information to the version number: + +[source,modelica] +---- +/*literal*/ constant String versionDate + "UTC date of first version build (in format: YYYY-MM-DD)"; +/*literal*/ constant Integer versionBuild + "Larger number is a more recent maintenance update"; +/*literal*/ constant String dateModified + "UTC date and time of the latest change to the package + in the following format (with one space between date + and time): YYYY-MM-DD hh:mm:ssZ"; +/*literal*/ constant String revisionId + "Revision identifier of the version management system used + to manage this library. It marks the latest submitted + change to any file belonging to the package"; +---- + +[example] +==== +Example: + +[source,modelica] +---- +package Modelica + ... + annotation( + version = "3.0.1", + versionDate = "2008-04-10", + versionBuild = 4, + dateModified = "2009-02-15 16:33:14Z", + revisionId = "$Id:: package.mo 2566 2009-05-26 13:25:54Z #$"); +end Modelica; + +model M1 + annotation( + uses(Modelica(version = "3.0.1"))); // Common case +end M1 + +model M2 + annotation( + uses(Modelica(version = "3.0.1", versionBuild = 4))); +end M2 +---- +==== + +The meanings of these annotations are: + +* `version` is the version number of the released library, see <>. + +* `versionDate` is the date in UTC format (according to ISO 8601) when the library was released. +This string is updated by the library author to correspond with the version number. + +* `versionBuild` is the optional build number of the library. +When a new version is released `versionBuild` should be omitted or `versionBuild = 1`. +There might be bug fixes to the library that do not justify a new library version. +Such maintenance changes are called a _build_ release of the library. +For every new maintenance change, the `versionBuild` number is increased. +A `versionBuild` number A that is higher than `versionBuild` number B, is a newer release of the library. +There are no conversions between the same versions with different build numbers. ++ +Two releases of a library with the same `version` but different `versionBuild` are in general assumed to be compatible. +In special cases, the `uses`-clause of a model may specify `versionBuild` and/or `dateModified`. +In such a case the tool is expected to give a warning if there is a mismatch between library and model. +* `dateModified` is the UTC date and time (according to ISO 8601) of the last modification of the package. ++ +[NOTE] +==== +The intention is that a Modelica tool updates this annotation whenever the package or part of it was modified and is saved on persistent storage (like file or database system). +==== +* `revisionId` is a tool specific revision identifier possibly generated by a source code management system (e.g., Subversion or CVS). +This information exactly identifies the library source code in the source code management system. + +The `versionBuild` and `dateModified` annotations can also be specified in the `uses` annotation (together with the version number). + +[NOTE] +==== +It is recommended that tools do not automatically store `versionBuild` and `dateModified` in the `uses` annotation. +==== + +=== Access Control to Protect Intellectual Property +:id: annotations-for-access-control-to-protect-intellectual-property + +This section presents annotations to define the protection and the licensing of packages. +The goal is to unify basic mechanisms to control the access to a package in order to protect the intellectual property contained in it. +This information is used to encrypt a package and bind it optionally to a particular target machine, and/or restrict the usage for a particular period of time. + +[NOTE] +==== +Protecting the intellectual property of a Modelica package is considerably more difficult than protecting code from a programming language. +The reason is that a Modelica tool needs the model equations in order that it can process the equations symbolically, as needed for acausal modeling. +Furthermore, if a Modelica tool generates C-code of the processed equations, this code is then potentially available for inspection by the user. +Finally, the Modelica tool vendors have to be trusted, that they do not have a backdoor in their tools to store the (internally) decrypted classes in human readable format. +The only way to protect against such misuse is legally binding warranties of the tool vendors. + +The intent of this section is to enable a library vendor to maintain one source version of their Modelica library that can be encrypted and used with several different Modelica tools, using different encryption formats. +==== + +The following definitions relate to access control. + +Defintion Protection:: +Define what parts of a class are visible. + +Defintion Obfuscation:: +Changing a Modelica class or generated code so that it is difficult to inspect by a user (e.g., by automatically renaming variables to non-meaningful names). + +Defintion Encryption:: +Encoding of a model or a package in a form so that the modeler cannot inspect any content of a class without an appropriate key. +An encrypted package that has the `Protection` annotation is read-only; the way to modify it is to generate a new encrypted version. + +Defintion Licensing:: +Restrict the use of an encrypted package for particular users for a specified period of time. + +In this section annotations are defined for protection and licensing. +Obfuscation and encryption are not standardized. + +Protection and licensing are both defined inside the `Protection` annotation: + +[source,modelica] +---- +annotation(Protection(...)); +---- + +==== Protection of Classes +:id: protection-of-classes + +A class may have the following annotations to define what parts of a class are visible, and only the parts explicitly listed as visible below can be accessed (if a class is encrypted and no `Protection` annotation is defined, the access annotation has the default value `Access.documentation`): + +[source,modelica] +---- +record Protection + /*literal*/ constant Access access; + ... +end Protection; +type Access = enumeration(hide, icon, documentation, diagram, + nonPackageText, nonPackageDuplicate, + packageText, packageDuplicate); +---- + +The other members of this record are given in <>. + +The items of the `Access` enumeration have the following meanings: + +. `Access.hide` + +Do not show the class anywhere (it is not possible to inspect any part of the class). + +. `Access.icon` + +The class can be instantiated and public parameter, constant, input, output variables as well as public connectors can be accessed, as well as the `Icon` annotation, as defined in <> (the declared information of these elements can be shown). +Additionally, the class name and its description text can be accessed. + +. `Access.documentation` + +Same as `Access.icon` and additionally the `Documentation` annotation (as defined in <>) can be accessed. +HTML-generation in the `Documentation` annotation is normally performed before encryption, but the generated HTML is intended to be used with the encrypted package. +Thus the HTML-generation should use the same access as the encrypted version -- even before encryption. + +. `Access.diagram` + +Same as `Access.documentation` and additionally, the `Diagram` annotation, and all components and `connect`-equations that have a graphical annotation can be accessed. + +. `Access.nonPackageText` + +Same as `Access.diagram` and additionally if it is not a package: the whole class definition can be accessed (but that text cannot be copied, i.e., you can see but not copy the source code). + +. `Access.nonPackageDuplicate` + +Same as `Access.nonPackageText` and additionally if it is not a package: the class, or part of the class, can be copied. + +. `Access.packageText` + +Same as `Access.diagram` (note: _not_ including all rights of `Access.nonPackageDuplicate`) and additionally the whole class definition can be accessed (but that text cannot be copied, i.e., you can see but not copy the source code). + +. `Access.packageDuplicate` + +Same as `Access.packageText` and additionally the class, or part of the class, can be copied. + +The `access` annotation holds for the respective class and all classes that are hierarchically on a lower level, unless overridden by a `Protection` annotation with `access`. +Overriding `access=Access.hide` or `access=Access.packageDuplicate` has no effect. + +[example] +==== +Example: If the annotation is given on the top level of a package and at no other class in this package, then the `access` annotation holds for all classes in this package. +==== + +Classes should not use other classes in ways that contradict this protection. +Tools must ensure that protected contents are not shown, even if classes do not meet this requirement. + +[example] +==== +Example: For instance a class with `Access.hide` should not be used in the diagram layer of a class with `Access.diagram`, and there should not be hyperlinks to classes with `Access.icon` (from classes with visible documentation). + +Consider the following invalid use of a class with `Access.hide`: + +[source,modelica] +---- +package P + block MySecret + RealOutput y = time; + annotation(Protection(access = Access.hide)); + end MySecret; + model M + MySecret mySecret + annotation(Placement( + transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}))); + Integrator integrator + annotation(Placement( + transformation(origin = {70, 30}, extent = {{-10, -10}, {10, 10}}))); + equation + connect(mySecret.y, integrator.u) + annotation(Line(origin = {49.5, 30}, points = {{-8.5, 0}, {8.5, -0}})); + annotation(Protection(access = Access.diagram)); + end M; + + model M2 + // The class MySecret is a simpler Modelica.Blocks.Sources.ContinuousClock + MySecret mySecret annotation(Placement( + transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}))); + annotation(Protection(access = Access.packageDuplicate)); + end M2; +end P; +---- + +In order to not reveal the existence of the class `P.MySecret` in `P.M`, a tool may choose to show the diagram of `P.M` with both `mySecret` and all connections to it removed. +(The tool could also choose to not show the diagram of `P.M` at all, or even reject to load the package `P` altogether.) +As long as the invalid use of `P.MySecret` occurs within the same top level package as where the class is defined (here, `P`), a tool is allowed to silently ignore the use for purposes of model translation. +When simulating `P.M`, the tool must not store `mySecret.y`. + +It is not specified whether a tool hides the entire text of `P.M2`, hides just the declaration, or shows the entire text of the `P.M2`. +In order to support development of valid protected packages, it is of course OK and expected that a tool will report the invalid use of `P.MySecret` in `P.M` and `P.M2` (revealing its existence in a diagnostic) during development of the package. +==== + +[example] +==== +Example: With the same package `P` as in the previous example, consider the following invalid use outside of `P`: + +[source,modelica] +---- +model My + // There exist a class P.MySecret + P.MySecret a + annotation(Placement( + transformation(origin = {30, 30}, extent = {{-10, -10}, {10, 10}}))); +end My; +---- + +Regardless of the protection of `My`, a tool must act as if `P.MySecret` did not exist. +For example, translation of `My` must fail with a standard error message about reference to the non-existing class `P.MySecret`. +Further, just like when being misused inside the package `P`, the tool must not reveal that it knows about the icon of `P.MySecret`. +With such precautions taken, showing the text or diagram of `My` is permitted as it doesn't reveal the actual existence of `P.MySecret`. +==== + +[NOTE] +==== +It is currently not standardized which result variables are accessible for plotting. +It seems natural to not introduce new flags for this, but reuse the `Access.XXX` definition. +For instance: + +* For `Access.icon` only the variables can be stored in a result file that can also be inspected in the class. + +* For `Access.nonPackageText` all public and protected variables can be stored in a result file, because all variables can be inspected in the class. + +[source,modelica] +---- +package CommercialFluid // Access icon, documentation, diagram + package Examples // Access icon, documentation, diagram + model PipeExample // Access everything, can be copied + end PipeExample; + + package Circuits // Access icon, documentation, diagram + model ClosedCircuit // Access everything, can be copied + end ClosedCircuit; + end Circuits; + + model SecretExample // No access + annotation(Protection(access=Access.hide)); + end SecretExample; + annotation(Protection(access=Access.nonPackageDuplicate)); + end Examples; + + package Pipe // Access icon + model StraightPipe // Access icon + end StraightPipe; + annotation(Protection(access=Access.icon)); + end Pipe; + + package Vessels // Access icon, documentation, diagram + model Tank // Access icon, documentation, diagram, text + end Tank; + end Vessels; + annotation(Protection(access=Access.nonPackageText)); +end CommercialFluid; +---- +==== + +==== Licensing +:id: licensing + +In this section annotations within the `Protection` annotation are defined to restrict the usage of the encrypted package: + +[source,modelica] +---- +record Protection + /*literal*/ constant Access access; + /*literal*/ constant String features[:] "Required license features"; + /*literal*/ constant UserLicense License; +end Protection; + +record UserLicense "Internal class name, do not use" + /*literal*/ constant String libraryKey; + /*literal*/ constant String licenseFile = "" "Default mapping if empty"; +end UserLicense; +---- + +See <> for a description of `access`. +The `License` annotation has only an effect on the top of an encrypted class and is then valid for the whole class hierarchy. +(Usually the licensed class is a package.) +The `libraryKey` is a secret string from the library vendor and is the protection mechanism so that a user cannot generate his/her own authorization file since the `libraryKey` is unknown to him/her. + +The `features` annotation defines the required license options. +If the features vector in the library has one or more elements, then at least a license feature according to one of the elements must be present in the authorization file. +If the features vector in the library is empty or not provided there is no need for a license feature in the authorization file. +As with the other annotations, the `features` annotation holds for the respective class and for all classes that are hierarchically on a lower level, unless further restricted by a corresponding annotation. +If no license according to the `features` annotation is provided in the authorization file, the corresponding classes are not visible and cannot be used, not even internally in the package. + +[example] +==== +Example: + +[source,modelica] +---- +// Requires license feature "LicenseOption" +annotation(Protection(features={"LicenseOption"})); + +// Requires license features "LicenseOption1" or "LicenseOption2" +annotation(Protection(features={"LicenseOption1", "LicenseOption2"})); + +// Requires license features ("LicenseOption1" and "LicenseOption2") or "LicenseOption3" +annotation(Protection(features={"LicenseOption1 LicenseOption2", "LicenseOption3"})); +---- +==== + +In order that the protected class can be used either a tool specific license manager, or an authorization file (called `licenseFile`) must be present. +The authorization file is standardized. +It is a Modelica package without classes that has an `Authorization` annotation of the following form which specifies a sequence of target records, which makes it natural to define start/end dates for different sets of targets individually: + +[source,modelica] +---- +record Authorization + /*literal*/ constant String licensor = "" "Information about the licensor"; + /*literal*/ constant String libraryKey "Matching the key in the class. Must be encrypted and not visible"; + /*literal*/ constant License license[:] "Definition of the license options and of the access rights"; +end Authorization; +---- + +using the following definition: + +[source,modelica] +---- +record License + /*literal*/ constant String licensee = "" "Information about the licensee"; + /*literal*/ constant String id[:] "Unique machine identifications, e.g., MAC addresses"; + /*literal*/ constant String features[:] "Activated library license features"; + /*literal*/ constant String startDate = "" "Start date in UTCformat YYYY-MM-DD"; + /*literal*/ constant String expirationDate = "" "Expiration date in UTCformat YYYY-MM-DD"; + /*literal*/ constant String operations[:] "Library usage conditions"; +end License; +---- + +If `startDate` and `expirationDate` are empty (the default) it is a perpetual license. + +The format of the strings used for `libraryKey` and `id` are not specified (they are vendor specific). +The `libraryKey` is a secret of the library developer. +The `operations` define the usage conditions and the following are default names: + +* `"ExportBinary"` Binary code generated from the Modelica code of the library can be included in binaries produced by a simulation tool. +* `"ExportSource"` Source code generated from the Modelica code of the library can be included in sources produced by a simulation tool. + +Additional tool-specific names can also be used. +To protect the `libraryKey` and the target definitions, the authorization file must be encrypted and must never show the `libraryKey`. + +[NOTE] +==== +All other information, especially licensor and license should be visible, in order that the user can get information about the license. +It is useful to include the name of the tool in the authorization file name with which it was encrypted. +Note, it is not useful to store this information in the annotation, because only the tool that encrypted the `Authorization` package can also decrypt it. +==== + +[example] +==== +Example: (Before encryption:) + +[source,modelica] +---- +// File MyLibrary\package.mo +package MyLibrary + annotation(Protection(License(libraryKey="15783-A39323-498222-444ckk4ll", + licenseFile="MyLibraryAuthorization_Tool.mo_lic"), ...)); +end MyLibrary; + +// File MyLibrary\MyLibraryAuthorization_Tool.mo\ +// (authorization file before encryption) +package MyLibraryAuthorization_Tool + annotation(Authorization( + libraryKey="15783-A39323-498222-444ckk4ll", + licensor ="Organization A\nRoad, Country", + license={ + License(licensee="Organization B, Mr.X", + id ={"lic:1269"}), // tool license number + License(licensee="Organization C, Mr. Y", + id ={"lic:511"}, expirationDate="2010-06-30", + operations={"ExportBinary"}), + License(licensee="Organization D, Mr. Z", + id ={"mac:0019d2c9bfe7"}) // MAC address + })); +end MyLibraryAuthorization_Tool; +---- +==== + +=== License Texts +:id: license-texts + +For a top-level class the `annotation(License="modelica:/TopPackage/Resources/Licenses/MyLicense.txt")`, gives a license text file for the class. +The annotation may give a scalar string or an array of strings, where each string is a URI referencing a license text file, see <>. +The license text files are human-readable files containing the license conditions. +If the annotation specifies an array of license text files the conditions in all of them must be satisfied (i.e., it is not intended for dual-licensing). +External functions may also contain this annotation (with similar content), see <>. + +When generating a distributable version for a translated model the license text files for used functions and top-level classes should be included. +Exceptions could be that licenses will be distributed in another way, or that there is an agreement to include different licenses (e.g., for internal libraries). +Note that the `License` annotation only allows the library developer and tool to assist the user in doing what is legally required; in the end it is a user responsibility to ensure that required license text files accompany distributable versions of models. + +=== Functions +:id: annotations-for-functions + +See <>, <>, and <>. + +=== Choices for Modifications and Redeclarations +:id: annotation-choices-for-modifications-and-redeclarations + +See <>. \ No newline at end of file diff --git a/docs/19__unitexpressions.adoc b/docs/19__unitexpressions.adoc new file mode 100644 index 000000000..cc0e0a02f --- /dev/null +++ b/docs/19__unitexpressions.adoc @@ -0,0 +1,112 @@ +== Unit Expressions +:id: unit-expressions + +Unless otherwise stated, the syntax and semantics of unit expressions in Modelica (for example, see <> or <>) conform with the international standards _International System of Units (SI)_ by BIPM superseding parts of ISO 31/0-1992 _General principles concerning quantities, units and symbols_ and ISO 1000-1992 _SI units and recommendations for the use of their multiples and of certain other units_. +Unfortunately, these standards do not define a formal syntax for unit expressions. +There are recommendations and Modelica exploits them. + +Note that this document uses the American spelling _meter_, whereas the SI specification from BIPM uses the British spelling _metre_. + +Examples for the syntax of unit expressions used in Modelica: `"N.m"`, `"kg.m/s2"`, `"kg.m.s-2"`, `"1/rad"`, `"mm/s"`. + +=== The Syntax of Unit Expressions + +The Modelica unit string syntax allows neither comments nor white-space, and a unit string shall match the `unit-expression` rule: + +[source,grammar] +---- +unit-expression : + unit-numerator [ "/" unit-denominator ] + +unit-numerator : + "1" | unit-factors | "(" unit-expression ")" + +unit-denominator: + unit-factor | "(" unit-expression ")" +---- + +The unit of measure of a dimension free quantity is denoted by `"1"`. +The SI standard does not define any precedence between multiplications and divisions. +The SI standard does not allow multiple units to the right of the division-symbol (`/`) since the result is ambiguous; either the divisor shall be enclosed in parentheses, or negative exponents used instead of division, for example, `"J/(kg.K)"` may be written as `"J.kg-1.K-1"`. + +[source,grammar] +---- +unit-factors : + unit-factor [ "." unit-factors ] +---- + +The SI standard specifies that a multiplication operator symbol is written as space or as a dot. +The SI standard requires that this _dot_ is a bit above the base line: `·`, which is not part of ASCII. +The ISO standard also prefers `·`, but Modelica supports the ISO alternative `.`, which is an ordinary _dot_ on the base line. + +For example, Modelica does not support `"Nm"` for newton-meter, but requires it to be written as `"N.m"`. + +[source,grammar] +---- +unit-factor : + unit-operand [ unit-exponent ] + +unit-exponent : + [ "+" | "-" ] ( UNSIGNED-INTEGER | "(" UNSIGNED-INTEGER "/" UNSIGNED-INTEGER ")" ) +---- + +The SI standard uses super-script for the exponentation, and does thus not define any operator symbol for exponentiation. +A `unit-factor` consists of a `unit-operand` possibly suffixed by a possibly signed integer or rational number, which is interpreted as an exponent. +There must be no spacing between the `unit-operand` and a possible `unit-exponent`. +It is recommended to use the simplest representation of exponents, meaning that the explicit `+` sign should be avoided, that leading zeros should be avoided, that rational exponents are reduced to not have common factors in the numerator and denominator, that rational exponents with denominator 1 should be avoided in favor of plain integer exponents, that the exponent 1 is omitted, and that entire factors with exponent 0 are omitted. + +[source,grammar] +---- +unit-operand : + unit-symbol | unit-prefix unit-symbol + +unit-prefix : + "Q" | "R" | "Y" | "Z" | "E" | "P" | "T" | "G" | "M" | "k" | "h" | "da" + | "d" | "c" | "m" | "u" | "n" | "p" | "f" | "a" | "z" | "y" | "r" | "q" + +unit-symbol : + unit-char { unit-char } + +unit-char : + NON-DIGIT +---- + +The units required to be recognized are the basic and derived units of the SI system, as well as some units compatible with the SI system listed below, but tools are allowed to additionally support user-defined unit symbols. +The required unit symbols do not make use of Greek letters, but a unit such as Ω is spelled out as `"Ohm"`. +Similarly degree is spelled out as `"deg"`, both on its own (for angles) and as part of `"degC"`, `"degF"` and `"degRk"` for temperatures (Celsius, Fahrenheit and Rankine). + +It is recommended that non-SI units are only used for the `displayUnit` attribute in order to reduce impact of unrecognized unit symbols when using another Modelica tool. + +The following are the units required to be recognized in addition to the SI system: + +* minute `"min"` (1 minute = 60 s) +* hour `"h"` (1 hour = 3600 s) +* day `"d"` (1 day = 86400 s) +* liter `"l"` and `"L"` (1 liter = 1 dm³) +* electronvolt `"eV"` (1 electronvolt = 1.602176634e-19 J) +* degree `"deg"` (1 degree = π/180 rad) +* debye `"debye"` (1 debye = 1e-21 / 299792458 Cm) + +The first 7 are listed in the SI standard as non-SI units that are acceptable to use with the SI system. + +A `unit-operand` should first be interpreted as a `unit-symbol` and only if not successful the second alternative assuming a prefixed operand should be exploited. +There must be no spacing between the `unit-symbol` and a possible `unit-prefix`. +The values of the prefixes are according to the ISO standard. +The letter `u` is used as a symbol for the prefix _micro_. + +[NOTE] +A tool may present `"Ohm"` as Ω and the prefix `"u"` as μ. +Exponents such as `"m2"` may be presented as m². +Degrees may be presented as °, both for `"deg"` on its own (for angles) and for temperatures -- e.g., `"degC"` can be presented as °C. +Note that BIPM have specific recommendations for formatting using these symbols. + +[example] +==== +Example: The unit expression `"m"` means meter and not milli (10⁻³), since prefixes cannot be used in isolation. +For millimeter use `"mm"` and for square meter, m², write `"m2"`. + +The expression `"mm2"` means (10⁻³m)² = 10⁻⁶m². +Note that exponentiation includes the prefix. + +The unit expression `"T"` means tesla, but note that the letter `T` is also the symbol for the prefix tera which has a multiplier value of 10¹². +==== \ No newline at end of file diff --git a/docs/1___introduction.adoc b/docs/1___introduction.adoc new file mode 100644 index 000000000..7e4844a59 --- /dev/null +++ b/docs/1___introduction.adoc @@ -0,0 +1,157 @@ +== Introduction + +=== Overview of Modelica + +Modelica is a language for modeling of cyber-physical systems, supporting acausal connection of components governed by mathematical equations to facilitate modeling from first principles. +It provides object-oriented constructs that facilitate reuse of models, and can be used conveniently for modeling complex systems containing, e.g., mechanical, electrical, electronic, magnetic, hydraulic, thermal, control, electric power or process-oriented subcomponents. + +=== Scope of the Specification + +The semantics of the Modelica language is specified by means of a set of rules for translating any class described in the Modelica language to a flat Modelica structure. +The semantic specification should be read together with the Modelica grammar. + +A class (of specialized class `model` or `block`) intended to be simulated on its own is called a _simulation model_. + +The flat Modelica structure is also defined for other cases than simulation models; including functions (can be used to provide algorithmic contents), packages (used as a structuring mechanism), and partial models (used as base-models). +This allows correctness to be verified for those classes, before using them to build the simulation model. + +There are specific semantic restrictions for a simulation model to ensure that the model is complete; they allow its flat Modelica structure to be further transformed into a set of differential, algebraic and discrete equations (= flat hybrid DAE). +Note that satisfying the semantic restrictions does not guarantee that the model can be initialized from the initial conditions and simulated. + +Modelica was designed to facilitate symbolic transformations of models, especially by mapping basically every Modelica language construct to equations in the flat Modelica structure. +Many Modelica models, especially in the associated Modelica Standard Library, are higher index systems, and can only be reasonably simulated if symbolic index reduction is performed, i.e., equations are differentiated and appropriate variables are selected as states, so that the resulting system of equations can be transformed to state space form (at least locally numerically), i.e., a hybrid DAE of index zero. +In order to allow this structural analysis, a tool may reject simulating a model if parameters cannot be evaluated during translation -- due to calls of external functions or initial equations/initial algorithms for `fixed = false` parameters. +Accepting such models is a quality of implementation issue. +The Modelica specification does not define how to simulate a model. +However, it defines a set of equations that the simulation result should satisfy as well as possible. + +The key issues of the translation (or flattening) are: + +* Expansion of inherited base classes. +* Parameterization of base classes, local classes and components. +* Generation of connection equations from `connect`-equations. + +The flat hybrid DAE form consists of: + +* Declarations of variables with the appropriate basic types, prefixes and attributes, such as `parameter Real v = 5`. +* Equations from equation sections. +* Function invocations where an invocation is treated as a set of equations which involves all input and all result variables (number of equations = number of basic result variables). +* Algorithm sections where every section is treated as a set of equations which involves the variables occurring in the algorithm section (number of equations = number of different assigned variables). +* The `when`-clauses where every `when`-clause is treated as a set of conditionally evaluated equations, which are functions of the variables occurring in the clause (number of equations = number of different assigned variables). + +Therefore, a flat hybrid DAE is seen as a set of equations where some of the equations are only conditionally evaluated. +Initial setup of the model is specified using `start`-attributes and equations that hold only during initialization. + +A Modelica class may also contain annotations, i.e., formal comments, which specify graphical representations of the class (icon and diagram), documentation text for the class, and version information. + +=== Some Definitions + +Explanations of many terms can be found using the document index in the document index. +Some important terms are defined below. + +Component:: +An element defined by the production `component-clause` in the Modelica grammar (basically a variable or an instance of a class) + +Element:: +Class definition, `extends`-clause, or `component-clause` declared in a class (basically a class reference or a component in a declaration). + +Flattening:: +The translation of a model described in Modelica to the corresponding model described as a hybrid DAE (see <>), involving expansion of inherited base classes, parameterization of base classes, local classes and components, and generation of connection equations from `connect`-equations. +In other words, mapping the hierarchical structure of a model into a set of differential, algebraic and discrete equations together with the corresponding variable declarations and function definitions from the model. + +Initialization:: +Simulation starts with solving the initialization problem at the starting time, resulting in values for all variables that are consistent with the result of the flattening. + +Transient analysis:: +Starting from the result of the initialization problem, the model is simulated forward in time. +This uses numerical methods for handling the hybrid DAE, resulting in solution trajectories for the model's variables, i.e., the value of the variables as a function of time. + +[NOTE] +In the numerical literature transient analysis is often called solving the initial value problem, but that term is not used here to avoid confusion with the initialization problem. + +Simulation:: +Simulation is the combination of initialization followed by transient analysis. + +[NOTE] +The model can be analyzed in ways other than simulation, e.g., linearization, and parameter estimation, but they are not described in the specification. + +Translation:: +Translation is the process of preparing a Modelica simulation model for simulation, starting with flattening but not including the simulation itself. + +[NOTE] +Typically, in addition to flattening, translation involves symbolic manipulation of the hybrid DAE and transforming the result into computer code that can simulate the model. + +// More terms that would be useful to define here: +// - deprecated +// - quality of implementation + +=== Notation + +The remainder of this section shows examples of the presentation used in this document. + +Syntax highlighting of Modelica code is illustrated by the code listing below. +Things to note include keywords that define code structure such as `equation`, keywords that do not define code structure such as `connect`, and recognized identifiers with meaning defined by the specification such as `semiLinear`: + +[source,modelica] +---- +model Example "Example used to illustrate syntax highlighting" + /* The string above is a class description string, this is a comment. */ + /* Invalid code is typically presented like this: */ + String s = 1.0; // Error: No conversion form Real to String. + Real x; +equation + 2 * x = semiLinear(time - 0.5, 2, 3); + /* The annotation below has omitted details represented by an ellipsis: */ + connect(resistor.n, conductor.p) annotation(...); +end Example; +---- + +Relying on implicit conversion of `Integer` literals to `Real` is common, as seen in the equation above (note use of Modelica code appearing inline in the text). + +It is common to mix Modelica code with mathematical notation. +For example, `average(x, y)` could be defined as latexmath:[\frac{x + y}{2}]. + +Inline code fragments are sometimes surrounded by quotes to clearly mark their beginning and end, or to emphasize separation from the surrounding text. +For example, `,` is used to separate the arguments of a function call. + +Something:: +Text defining the meaning of _something_. + +In addition to the style of definition above, new terminology can be introduced in the running text. +For example, a dummy is something that... + +[NOTE] +This is non-normative content that provides some explanation, motivation, and/or additional things to keep in mind. +It has no defining power and may be skipped by readers strictly interested in just the definition of the Modelica language. + +[example] +==== +This is an example, which is a special kind of non-normative content. +Examples often contain a mix of code listings and explanatory text, and this is no exception: + +[source,modelica] +---- +String s = 1.0; // Error: No conversion form Real to String. +---- + +To fix the type mismatch above, the number has to be replaced by a `String` expression, such as `"1.0"`. +==== + +Other code listings in the document include specification of lexical units and grammatical structure, both using metasymbols of the extended BNF-grammar defined in <>. +Lexical units are named with all upper-case letters and introduced with the `=` sign: + +[source,grammar] +---- +SOME-TOKEN = NON-DIGIT { DIGIT | NON-DIGIT } +---- + +Grammatical structure is recognized by production rules being named with lower-case letters and introduced with the `:` sign (also note appearance of the Modelica keyword `der`): + +[source,grammar] +---- +differentiated-expression : + der "(" SOME-TOKEN ")" + | "(" differentiated-expression "+" differentiated-expression ")" +---- + +Annotations are defined using the syntactic forms of Modelica record definitions and component declarations, but with special semantics given in <>. \ No newline at end of file diff --git a/docs/20__library.adoc b/docs/20__library.adoc new file mode 100644 index 000000000..2f7cdc2d3 --- /dev/null +++ b/docs/20__library.adoc @@ -0,0 +1,10 @@ +== The Modelica Standard Library +:id: the-modelica-standard-library + +In order that a modeler can quickly build up system models, it is important that libraries of the most commonly used components are available, ready to use, and sharable between applications. +For this reason, the Modelica Association develops and maintains a growing _Modelica Standard Library_ called `package Modelica`. +For an overview of the current version see https://github.com/modelica/ModelicaStandardLibrary. +This is a free library that can be used without essential restrictions, e.g., in commercial Modelica simulation environments. +The Modelica Standard Library is tool-neutral, and relies on a small library, ModelicaServices, that each conformant tool must implement to handle tool-specific couplings, e.g., for animation. +Furthermore, other people and organizations are developing free and commercial Modelica libraries. +For information about these libraries and for downloading the free libraries see https://modelica.org/libraries/. \ No newline at end of file diff --git a/docs/2___lexicalstructure.adoc b/docs/2___lexicalstructure.adoc new file mode 100644 index 000000000..639bfa21c --- /dev/null +++ b/docs/2___lexicalstructure.adoc @@ -0,0 +1,255 @@ +== Lexical Structure +:id: lexical-structure + +This chapter describes several of the basic building blocks of Modelica such as characters and lexical units including identifiers and literals. +Without question, the smallest building blocks in Modelica are single characters belonging to a character set. +Characters are combined to form lexical units, also called tokens. +These tokens are detected by the lexical analysis part of the Modelica translator. +Examples of tokens are literals, identifiers, and operators. +Comments are not really lexical units since they are eventually discarded. +On the other hand, comments are detected by the lexical analyzer before being thrown away. + +The information presented here is derived from the more formal specification in <>. + +=== Character Set + +The character set of the Modelica language is Unicode, but restricted to the Unicode characters corresponding to 7-bit ASCII characters for identifiers; see <>. + +=== Comments + +There are two kinds of comments in Modelica which are not lexical units in the language and therefore are treated as white-space by a Modelica translator. +The white-space characters are space, tabulator, and line separators (carriage return and line feed); and white-space cannot occur inside tokens, e.g., `+<=+` must be written as two characters without space or comments between them. +The following comment variants are available: + +[source,modelica] +---- +// Rest-of-line comment: Everything from // to the end of the line are ignored. +"Not part of comment" +/* Delimited comment: Characters after /* are ignored, + including line termination. The comment ends with */ +---- + +[NOTE] +The comment syntax is identical to that of C++. + +Delimited Modelica comments do not nest, i.e., `+/* */+` cannot be embedded within `+/* ... */+`. +The following is _invalid_: + +[source,modelica] +---- +/* Invalid nesting of comments, the comment ends just before 'end' +model Interesting + /* To be done */ +end Interesting; +*/ +---- + +Rest-of-line comments can safely be used to comment out blocks of code without risk of conflict with comments inside. + +[source,modelica] +---- +//model Valid // Some other comment +// /* To be done */ +//end Valid; +---- + +There is also a description-string, that is part of the Modelica language and therefore not ignored by the Modelica translator. +Such a description-string may occur at the end of a declaration, equation, or statement or at the beginning of a class definition. +For example: + +[source,modelica] +---- +model TempResistor "Temperature dependent resistor" + ... + parameter Real R "Resistance for reference temp."; + ... +end TempResistor; +---- + +=== Identifiers, Names, and Keywords + +_Identifiers_ are sequences of letters, digits, and other characters such as underscore, which are used for _naming_ various items in the language. +Certain combinations of letters are _keywords_ represented as _reserved_ words in the Modelica grammar and are therefore not available as identifiers. + +==== Identifiers + +Modelica _identifiers_, used for naming classes, variables, constants, and other items, are of two forms. +The first form always starts with a letter or underscore (`_`), followed by any number of letters, digits, or underscores. +Case is significant, i.e., the identifiers `Inductor` and `inductor` are different. +The second form (`Q-IDENT`) starts with a single quote, followed by a sequence of any printable ASCII character, where single-quote must be preceded by backslash, and terminated by a single quote, e.g., `'12H'`, `+'13\'H'+`, `'+foo'`. +Control characters in quoted identifiers have to use string escapes. +The single quotes are part of the identifier, i.e., `'x'` and `x` are distinct identifiers. +The redundant escapes (`'\?'` and `+'"'+`) are the same as the corresponding non-escaped variants (`'?'` and `'"'`), but are only for use in Modelica source code. +A full BNF definition of the Modelica syntax and lexical units is available in <>. + +[source,grammar] +---- +IDENT = NON-DIGIT { DIGIT | NON-DIGIT } | Q-IDENT +Q-IDENT = "'" { Q-CHAR | S-ESCAPE } "'" +NON-DIGIT = "_" | letters "a" ... "z" | letters "A" ... "Z" +DIGIT = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" +Q-CHAR = NON-DIGIT | DIGIT | "!" | "#" | "$" | "%" | "&" | "(" | ")" + | "*" | "+" | "," | "-" | "." | "/" | ":" | ";" | "<" | ">" | "=" + | "?" | "@" | "[" | "]" | "^" | "{" | "}" | "|" | "~" | " " | """ +S-ESCAPE = "\'" | "\"" | "\?" | "\\" + | "\a" | "\b" | "\f" | "\n" | "\r" | "\t" | "\v" +---- + +==== Names + +A _name_ is an identifier with a certain interpretation or meaning. +For example, a name may denote an `Integer` variable, a `Real` variable, a function, a type, etc. +A name may have different meanings in different parts of the code, i.e., different scopes. +The interpretation of identifiers as names is described in more detail in <>. +The meaning of package names is described in more detail in <>. + +[example] +Example: A name: `Ele.Resistor` + +A _component reference_ is an expression containing a sequence of identifiers and indices. +A component reference is equivalent to the referenced object, which must be a component. +A component reference is resolved (evaluated) in the scope of a class (see <>), or expression for the case of a local iterator variable (see <>). + +[example] +Example: A component reference: `Ele.Resistor.u[21].r` + +==== Modelica Keywords + +The following Modelica _keywords_ are reserved words that cannot be used where `IDENT` is expected in the language grammar (see <>): + +[cols="5*a",options=autowidth] +|=== +|`algorithm` |`discrete` |`false` |`loop` |`pure` +|`and` |`each` |`final` |`model` |`record` +|`annotation` |`else` |`flow` |`not` |`redeclare` +| |`elseif` |`for` |`operator` |`replaceable` +|`block` |`elsewhen` |`function` |`or` |`return` +|`break` |`encapsulated` |`if` |`outer` |`stream` +|`class` |`end` |`import` |`output` |`then` +|`connect` |`enumeration` |`impure` |`package` |`true` +|`connector` |`equation` |`in` |`parameter` |`type` +|`constant` |`expandable` |`initial` |`partial` |`when` +|`constrainedby` |`extends` |`inner` |`protected` |`while` +|`der` |`external` |`input` |`public` |`within` +|=== + +In particular, it is not allowed to declare an element or enumeration literal with these names. +This also applies to the identifiers that name the predefined types `Real`, `Integer`, `Boolean`, and `String`, see <>. + +[example] +==== +Example: Not all predefined types have names with restrictions: +[source,modelica] +---- +type StateSelect = enumeration(one, two); +StateSelect s = StateSelect.one; // OK, using local StateSelect. +Real x(stateSelect = StateSelect.never); // Error: 'never' is not a literal + // of StateSelect. +Real y(stateSelect = .StateSelect.never); // OK, using predefined StateSelect. +---- +==== + +=== Literals + +_Literals_ are unnamed constants used to build expressions, and have different forms depending on their type. +Each of the predefined types in Modelica has a way of expressing unnamed constants of the corresponding type, which is presented in the ensuing subsections. +Additionally, array literals and record literals can be expressed. + +==== Floating Point Numbers + +A floating point number is expressed as a decimal number in the form of a sequence of decimal digits followed by a decimal point, followed by decimal digits, followed by an exponent indicated by `E` or `e` followed by a sign and one or more decimal digits. +The various parts can be omitted, see `UNSIGNED-REAL` in <> for details and also the examples below. +The minimal recommended range is that of IEEE double precision floating point numbers, for which the largest representable positive number is 1.7976931348623157{nbsp}×{nbsp}10^308^ and the smallest positive number is 2.2250738585072014{nbsp}×{nbsp}10^-308^. +For example, the following are floating point number literals: + +[source,modelica] +---- +22.5, 3.141592653589793, 1.2E-35 +---- + +The same floating point number can be represented by different literals. +For example, all of the following literals denote the same number: + +[source,modelica] +---- +13., 13E0, 1.3e1, 0.13E2, .13E2 +---- + +The last variant shows that the leading zero is optional (in that case decimal digits must be present). +Note that `13` is not in this list, since it is not a floating point number, but can be converted to a floating point number. + +==== Integer Literals + +Literals of type `Integer` are sequences of decimal digits, e.g., as in the integer numbers `33`, `0`, `100`, `30030044`. +The range of supported `Integer` literals shall be at least large enough to represent the largest positive IntegerType value, see <>. + +[NOTE] +Negative numbers are formed by unary minus followed by an integer literal. + +==== Boolean Literals + +The two `Boolean` literal values are `true` and `false`. + +==== Strings + +String literals appear between double quotes as in `"between"`. +Any character in the Modelica language character set (see <> for allowed characters) apart from double quote (`"`) and backslash (`\`), including new-line, can be _directly_ included in a string without using an escape sequence. +Certain characters in string literals can be represented using escape sequences, i.e., the character is preceded by a backslash (`\`) within the string. +Those characters are: + +[cols="^a,a",options=autowidth] +|=== +|Character |Description + +|`\'` |Single quote, may also appear without backslash in string constants +|`\"` |Double quote +|`\?` |Question-mark, may also appear without backslash in string constants +|`\\` |Backslash itself +|`\a` |Alert (bell, code 7, ctrl-G) +|`\b` |Backspace (code 8, ctrl-H) +|`\f` |Form feed (code 12, ctrl-L) +|`\n` |Newline (code 10, ctrl-J), same as literal newline +|`\r` |Carriage return (code 13, ctrl-M) +|`\t` |Horizontal tab (code 9, ctrl-I) +|`\v` |Vertical tab (code 11, ctrl-K) +|=== + +For example, a string literal containing a tab, the words: _This is_, double quote, space, the word: _between_, double quote, space, the word: _us_, and new-line, would appear as follows: + +[source,modelica] +---- +"\tThis is\" between\" us\n" +---- + +Concatenation of string literals in certain situations (see the Modelica grammar) is denoted by the `+` operator in Modelica, e.g., `"a" + "b"` becomes `"ab"`. +This is useful for expressing long string literals that need to be written on several lines. + +The `"\n"` character is used to conceptually indicate the end of a line within a Modelica string. +Any Modelica program that needs to recognize line endings can check for a single `"\n"` character to do so on any platform. +It is the responsibility of a Modelica implementation to make any necessary transformations to other representations when writing to or reading from a text file. + +[NOTE] +For example, a `"\n"` is written and read as-is in a Unix or Linux implementation, but written as `"\r\n"` pair, and converted back to `"\n"` when read in a Windows implementation. + +[NOTE] +==== +For long string comments, e.g., the `info` annotation to store the documentation of a model, it would be very inconvenient, if the string concatenation operator would have to be used for every line of documentation. +It is assumed that a Modelica tool supports the non-printable newline character when browsing or editing a string literal. +For example, the following statement defines one string that contains (non-printable) newline characters: + +[source,modelica] +---- +assert(noEvent(length > s_small), +"The distance between the origin of frame_a and the origin of frame_b +of a LineForceWithMass component became smaller as parameter s_small +(= a small number, defined in the +\"Advanced\" menu). The distance is +set to s_small, although it is smaller, to avoid a division by zero +when computing the direction of the line force.", + level = AssertionLevel.warning); +---- +==== + +=== Operator Symbols + +The predefined operator symbols are formally defined in <> and summarized in the table of operators in <>. \ No newline at end of file diff --git a/docs/3___operatorsandexpressions.adoc b/docs/3___operatorsandexpressions.adoc new file mode 100644 index 000000000..41dd2c56c --- /dev/null +++ b/docs/3___operatorsandexpressions.adoc @@ -0,0 +1,1747 @@ +== Operators and Expressions +:id: operators-and-expressions + +The lexical units are combined to form even larger building blocks such as expressions according to the rules given by the `expression` part of the Modelica grammar in <>. +For example, they can be built from operators, function references, components, or component references (referring to components) and literals. +Each expression has a type and a variability. + +This chapter describes the evaluation rules for expressions, the concept of expression variability, built-in mathematical operators and functions, and the built-in special Modelica operators with function syntax. + +Expressions can contain variables and constants, which have types, predefined or user defined. +The predefined built-in types of Modelica are `Real`, `Integer`, `Boolean`, `String`, and enumeration types which are presented in more detail in <>. + +=== Expressions + +Modelica equations, assignments and declaration equations contain expressions. + +Expressions can contain basic operations, `+`, `-`, `*`, `/`, `^`, etc. with normal precedence as defined in <> in <> and the grammar in <>. +The semantics of the operations is defined for both scalar and array arguments in <>. + +It is also possible to define functions and call them in a normal fashion. +The function call syntax for both positional and named arguments is described in <> and for vectorized calls in <>. +The built-in array functions are given in <> and other built-in operators in <>. + +=== Operator Precedence and Associativity + +Operator precedence determines the implicit subexpression structure of expressions with operators. +(Explicit subexpression structure can be expressed by wrapping the subexpression in parentheses.) +An operator with higher precedence ties harder to its operands than an operator with lower precedence. +For example, `*` having higher precedence than `+` means that `1 + 2 * 3` is implicitly structured as `1 + (2 * 3)`. + +Precedence group associativity is used to determine the implicit subexpression structure when operators belong to the same group of equal precedence. +Left associativity means that subexpressions are formed from left to right. +For example, left associativity of binary additive operators means that `1 - 2 - 3` is implicitly structured as `(1 - 2) - 3`. +A precedence group may also be non-associative, meaning that there is no implicit subexpression structure defined based on associativity. +For example, non-associativity of relational operators means that `1 < 2 < 3` is an invalid expression. +Note that the operators don't need to be identical for associativity to matter; also `1 == 2 < 3` is invalid, and `1 - 2 + 3` is implicitly structured as `(1 - 2) + 3`. +Also note that the non-associative array range in Modelica can be used with either two or three operands separated by `:`, meaning that `1 : 2 : 5` is one valid ternary use of the operator rather than two invalid binary uses of the operator. + +At the parsing stage -- which is where the here defined operator precedence and associativity matters -- the subexpression structure is fixed. +Since Modelica tools have the freedom to symbolically manipulate expressions, this subexpression structure cannot be expected to reflect order of evaluation, compare <>. + +The following table presents the precedence and associativity of all the expression operators, consistent with and complementing information that can be derived from the Modelica grammar in <>. + + + +[[tab:operator-precedence,Table Operator Prececence]] +.Operators in order of precedence from highest to lowest. Operators with different precedence are separated by horizontal lines. All operators are binary except array index, member access, function call, those shown as unary together with `expr`, the conditional operator, the array construction operator `{ }` and concatenation operator `[ ]`, and the array range constructor which is either binary or ternary.+++
+++^†^{nbsp}The associativity of array construction and concatenation refers to the separator (`,` or `;`), not the enclosing delimiters. +[cols="a,^a,a,a"] +|=== +|Operator group |Assoc. |Operator syntax |Examples + +|Array index |left |`[]` |`arr[index]` +|Member access |left |`.` |`a.b` +|Function call |none |`funcName(args)` |`sin(4.36)` +|Array construction |left^†^ |`{expr, expr, ...}` |`{2, 3}` +|Horizontal concatenation |left^†^ |`[expr, expr, ...]` |`[5, 6]` +|Vertical concatenation |left^†^ |`[expr; expr; ...]` |`[2, 3; 7, 8]` +|Exponentiation |none |`^` |`2 ^ 3` +|Multiplicative |left |`* /` |`2 * 3`, `2 / 3` +|Elementwise multiplicative |left |`.* ./` |`{2, 3} .* {4, 5}` +|Additive unary |none |`+expr -expr` |`-0.5` +|Additive |left |`+ -` |`1 + 2` +|Elementwise additive |left |`. + .-` |`{2, 3} .+ {4, 5}` +|Relational |none |`< <= > >= == <>` |`a < b`, `a <= b`, `a > b` +|Unary negation |none |`not expr` |`not b1` +|Logical and |left |`and` |`b1 and b2` +|Logical or |left |`or` |`b1 or b2` +|Array range |none |`expr : expr` |`1 : 5` +|Array range |none |`expr : expr : expr` |`start : step : stop` +|Conditional |none |`if expr then expr else expr` |`if b then 3 else x` +|Named argument |none |`ident = expr` |`x = 2.26` +|=== + +The array index and member access operators can both be part of a `component-reference` (one of the alternative productions for `primary` in the grammar) and be applied to general expressions when the left operand is parenthesized. +Directly using both member access and array index in a `component-reference` has a special intuitive meaning. +See <> and <>. + +[example] +==== +Example: Relative precedence of array index and member access. +Consider the following definition of the array variable `a`: +[source,modelica] +---- +record R + Real[2] x; +end R; +R[3] a; +---- + +These are some valid as well as invalid ways to using array index and member access: + +[source,modelica] +---- +a[3].x[2] // OK: Component reference of type Real +a[3].x // OK: Component reference of type Real[2] +a.x[2] // OK: Component reference of type Real[3] +a.x[2, :] // Error. +a.x // OK: Component reference of type Real[3, 2] +(a.x)[2] // OK: Component reference of type Real[2] - same as a[2].x[:] +(a.x)[2, :] // OK: Component reference of type Real[2] - same as a[2].x[:] +a[3] // OK: Component reference of type R +(a[3]).x // OK: Like a[3].x, but not a component reference +(a[3]).x[1] // Error. +((a[3]).x)[1] // OK: Like a[3].x[1], but not a component reference +---- + +The relation between `a.x`, `a.x[2]`, and `(a.x)[2]` illustrates the effect of giving higher precedence to array index than member access. +Had the precedence been equal, this would have changed the meaning of `a.x[2]` to the same thing that `(a.x)[2]` expresses, being a component reference of type `Real[2]`. +==== + +[example] +==== +Example: Non-associative exponentiation and array range operator (note that the array range operator only takes scalar operands): + +[source,modelica] +---- +x ^ y ^ z // Not legal, use parentheses to make it clear. +a : b : c : d // Not legal, and parentheses cannot make it legal. +---- +==== + +The additive unary expressions are only allowed in the first term of a sum, that is, not immediately to the right of any of the additive or elementwise additive operators. +For example, `1 + -1 + 1` is an invalid expression (not parseable according to <>), whereas both `1 + (-1) + 1` and `-1 + 1 + 1` are fine. + +[example] +==== +Example: The unary minus and plus in Modelica is slightly different than in Mathematicafootnote:[_Mathematica_ is a registered trademark of Wolfram Research Inc.] and in MATLABfootnote:[_MATLAB_ is a registered trademark of MathWorks Inc. +], since the following expressions are illegal (whereas in Mathematica and in MATLAB these are valid expressions): +[source,modelica] +---- +2*-2 // = -4 in Mathematica/MATLAB; is illegal in Modelica +--2 // = 2 in Mathematica/MATLAB; is illegal in Modelica +++2 // = 2 in Mathematica/MATLAB; is illegal in Modelica +2--2 // = 4 in Mathematica/MATLAB; is illegal in Modelica +---- +==== + +The conditional operator may also include `elseif`-branches. + +Equality `=` and assignment `:=` are not expression operators since they are allowed only in equations and in assignment statements respectively. + +[NOTE] +The operator precedence table is useful when generating textual representations of Modelica expression trees. +When doing this, attention must be paid to the rule that the unary additive operators are only allowed for the first term in a sum. +A naive implementation might not produce all the required parentheses for an expression tree such as `1 + (-1)`, as it might think that the higher precedence of the unary operator makes the parentheses redundant. +A trick that solves this problem is to instead treat the additive unary operators as left associative with the same precedence as the binary additive operators. + +=== Evaluation Order + +A tool is free to solve equations, reorder expressions and to not evaluate expressions if their values do not influence the result (e.g., short-circuit evaluation of `Boolean` expressions). +`if`-statements and `if`-expressions guarantee that their branches are only evaluated if the appropriate condition is true, but relational operators generating state or time events will during continuous integration have the value from the most recent event. + +If a numeric operation overflows the result is undefined. +For literals it is recommended to automatically convert the number to another type with greater precision. + +[example] +==== +Example: If one wants to guard an expression against incorrect evaluation, it should be guarded by an `if`: + +[source,modelica] +---- + Boolean v[n]; + Boolean b; + Integer I; +equation + b = (I >= 1 and I <= n) and v[I]; // Unsafe, may result in error + b = if (I >= 1 and I <= n) then v[I] else false; // Safe +---- + +To guard square against square root of negative number use `noEvent`: + +[source,modelica] +---- +der(h) = if h > 0 then -c * sqrt(h) else 0; // Incorrect +der(h) = if noEvent(h > 0) then -c * sqrt(h) else 0; // Correct +---- +==== + +=== Arithmetic Operators + +Modelica supports five binary arithmetic operators that operate on any numerical type: + +[cols="^a,l",options=autowidth] +|=== +|Operator |Description + +|`^` |Exponentiation +|`*` |Multiplication +|`/` |Division +|`+` |Addition +|`-` |Subtraction +|=== + +Some of these operators can also be applied to a combination of a scalar type and an array type, see <>. + +The syntax of these operators is defined by the following rules from the Modelica grammar: +[source,grammar] +---- +arithmetic-expression : + [ add-operator ] term { add-operator term } + +add-operator : + "+" | "-" + +term : + factor { mul-operator factor } + +mul-operator : + "*" | "/" + +factor : + primary [ "^" primary ] +---- + +=== Equality, Relational, and Logical Operators + +Modelica supports the standard set of relational and logical operators, all of which produce the standard boolean values `true` or `false`: + +[cols="^a,a",options=autowidth] +|=== +|Operator |Description + +|`>` |Greater than +|`>=` |Greater than or equal +|`<` |Less than +|`+<=+` |Less than or equal to +|`==` |Equality within expressions +|`<>` |Inequality +|=== + +A single equals sign `=` is never used in relational expressions, only in equations (see <>, <>) and in function calls using named parameter passing (see <>). + +The following logical operators are defined: + +[cols="^a,a",options=autowidth] +|=== +|Operator |Description + +|`not` |Logical negation (unary operator) +|`and` |Logical _and_ (conjunction) +|`or` |Logical _or_ (disjunction) +|=== + +The grammar rules define the syntax of the relational and logical operators. +[source,grammar] +---- +logical-expression : + logical-term { or logical-term } + +logical-term : + logical-factor { and logical-factor } + +logical-factor : + [ not ] relation + +relation : + arithmetic-expression [ relational-operator arithmetic-expression ] + +relational-operator : + "<" | "<=" | ">" | ">=" | "==" | "<>" +---- + +The following holds for relational operators: + +* Relational operators `<`, `+<=+`, `>`, `>=`, `==`, `<>`, are only defined for scalar operands of simple types. + The result is `Boolean` and is true or false if the relation is fulfilled or not, respectively. + +* For operands of type `String`, `str1` _op_ `str2` is for each relational operator, _op_, defined in terms of the C function `strcmp` as `strcmp(str1, str2)` _op_ `0`. + +* For operands of type `Boolean`, `false < true`. + +* For operands of enumeration types, the order is given by the order of declaration of the enumeration literals. + +* In relations of the form `v1 == v2` or `v1 <> v2`, `v1` or `v2` shall, unless used in a function, not be a subtype of `Real`. ++ +[NOTE] +The reason for this rule is that relations with `Real` arguments are transformed to state events (see <>) and this transformation becomes unnecessarily complicated for the `==` and `<>` relational operators (e.g., two crossing functions instead of one crossing function needed, epsilon strategy needed even at event instants). +Furthermore, testing on equality of `Real` variables is questionable on machines where the number length in registers is different to number length in main memory. + +* Relational operators can generate events, see <>. + +=== Miscellaneous Operators and Variables + +Modelica also contains a few built-in operators which are not standard arithmetic, relational, or logical operators. +These are described below, including `time`, which is a built-in variable, not an operator. + +==== String Concatenation + +Concatenation of strings (see the Modelica grammar) is denoted by the `+` operator in Modelica. + +[example] +Example: `"a" + "b"` becomes `"ab"`. + +==== Array Constructor Operator + +The array constructor operator `{ ... }` is described in <>. + +==== Array Concatenation Operator + +The array concatenation operator `[ ... ]` is described in <>. + +==== Array Range Operator + +The array range constructor operator `:` is described in <>. + +==== If-Expressions + +An expression + +[source,modelica] +---- +if expression1 then expression2 else expression3 +---- + +is one example of `if`-expression. +First `expression1`, which must be `Boolean` expression, is evaluated. +If `expression1` is true `expression2` is evaluated and is the value of the `if`-expression, else `expression3` is evaluated and is the value of the `if`-expression. +The two expressions, `expression2` and `expression3`, must be type compatible expressions (see <>) giving the type of the `if`-expression. +The `if`-expressions with `elseif` are defined by replacing `elseif` by `else if`. +For short-circuit evaluation see <>. + +[NOTE] +`elseif` in expressions has been added to the Modelica language for symmetry with `if`-equations. + +[example] +==== +Example: + +[source,modelica] +---- +Integer i; +Integer sign_of_i1 = if i < 0 then -1 elseif i == 0 then 0 else 1; +Integer sign_of_i2 = if i < 0 then -1 else if i == 0 then 0 else 1; +---- +==== + +==== Member Access Operator + +It is possible to access members of a class instance using dot notation, i.e., the `.` operator. +It is also possible to select a record member of a general expression by enclosing it in parentheses. +Note that while the selection is applied to an `output-expression-list` in the grammar, it is only semantically valid when the `output-expression-list` represents an expression. + +In case the first operand is an array it is seen as a slicing operation, see <>. + +[example] +==== +Example: The component reference `R1.R` accesses the resistance component `R` of resistor `R1`. + +The qualified class name `A.B` is a reference to the local class `B` which is a member of the class `A`. +Note that the left operand in this case is a class, not an instance of the class. + +`(Complex(2, 3)).re` constructs the record `Complex(2, 3)` and then selects the `re` component in it. +`Complex(2, 3).re` is not valid syntax. +==== + +==== Built-in Variable time + +All declared variables are functions of the independent variable `time`. +The variable `time` is a built-in variable available in all models and blocks, which is treated as an input variable. +It is implicitly defined as: + +[source,modelica] +---- +input Real time (final quantity = "Time", + final unit = "s"); +---- + +The value of the `start`-attribute of `time` is set to the time instant at which the simulation is started. + +[example] +==== +Example: + +[source,modelica] +---- +encapsulated model SineSource + import Modelica.Math.sin; + connector OutPort = output Real; + OutPort y = sin(time); // Uses the built-in variable time. +end SineSource; +---- +==== + +=== Built-in Operators and Functions + +Certain built-in operators of Modelica are called using the same syntax as a function call. +However, they do not behave as a mathematical function, because the result depends not only on the input arguments but also on the status of the simulation. + +There are also built-in functions that depend only on the input argument, but also may trigger events in addition to returning a value. +The built-in functions may also be overloaded such that a single Modelica function cannot be compatible with all calls of the function. +Here, _built-in_ means that they are defined at the Modelica language level, not through a Modelica `function` definition. +The following built-in operators/functions are available: + +* Mathematical functions and conversion operators, see <> below. +* Derivative and special purpose operators with function syntax, see <> below. +* Event-related operators, see <> below. +* Array operators/functions, see <>. +* Synchronous operators, see <>. +* State machine operators, see <>. + +Except where shadowing problems are being discussed, references to built-in functions and operators within this document always assume that the built-in definitions are not shadowed by user-defined definitions, see also <>. +With the exception where inputs are named (e.g., `String`), all operators and functions in this section can only be called with positional arguments. + +==== Numeric Functions and Conversion Operators + +The mathematical functions and conversion operators listed below do not generate events. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`abs(v)` |Absolute value (event-free) |<> +|`sign(v)` |Sign of argument (event-free) |<> +|`min(x, y)` |Least of two scalars (event-free) |<> +|`max(x, y)` |Greatest of two scalars (event-free) |<> +|`sqrt(v)` |Square root |<> +|`nthRoot(v, n)` |__n__th root |<> +|`Integer(e)` |Conversion from enumeration to `Integer` |<> +|`EnumTypeName(i)` |Conversion from `Integer` to enumeration |<> +|`String(...)` |Conversion to `String` |<> +|=== + +Except for the `min`, `max` and the `String` conversion operator they are vectorizable according to <>. +The `min` and `max` functions have array-specific variants which perform array reduction operations described in <>. + +Additional non-event generating mathematical functions are described in <>, whereas the event-triggering mathematical functions are described in <>. + +[[abs,Function abs]] +Function abs:: ++ +[source,modelica] +---- +abs(v) +---- ++ +Expands into `noEvent(if v >= 0 then v else -v)`. +Argument `v` needs to be an `Integer` or `Real` expression. + +[NOTE] +==== +By not generating events the property `abs(x) >= 0` for all `x` is ensured at the cost of sometimes having a derivative that changes discontinuously between events. + +A typical case requiring the event-free semantics is a flow equation of the form `abs(x) * x = y`. +With event generation, the equation would switch between the two forms `x^2 = y` and `-x^2 = y` at the events, where the events would not be coinciding exactly with the sign changes of `y`. +When `y` passes through zero, neither form of the equation would have a solution in an open neighborhood of `y = 0`, and hence solving the equation would have to fail at some point sufficiently close to `y = 0`. +Without event generation, on the other hand, the equation can be solved easily for `x`, also as `y` passes through zero. +Note that without event generation the derivative of `abs(x) * x` never changes discontinuously, despite `abs(x)` having a discontinuous derivative. + +In inverted form this equation is `x = sign(y) * sqrt(abs(y))`. +With event generation, the call to `sqrt` would fail when applied to a negative number during root finding of the zero crossing for `abs(y)`, compare <>. +Without event generation, on the other hand, evaluating `sqrt(abs(y))` will never fail. +==== + +[[sign,Function sign]] +Function sign:: ++ +[source,modelica] +---- +sign(v) +---- ++ +Expands into `noEvent(if v > 0 then 1 else if v < 0 then -1 else 0)`. +Argument `v` needs to be an `Integer` or `Real` expression. + +[[sqrt,Function sqrt]] +Function sqrt:: ++ +[source,modelica] +---- +sqrt(v) +---- ++ +Square root of `v`, equivalent to `nthRoot(v, 2)`. + +[[nthRoot,Function nthRoot]] +Function nthRoot:: ++ +[source,modelica] +---- +nthRoot(v, n) +---- ++ +__n__th root of `v`, where `v` shall be a `Real` expression, and `n > 0` shall be an `Integer` expression. +The result `y` is a real root of the equation `y^n = v`. +If `n` is even, `v` must be non-negative and `y` shall be the non-negative root. +(If `n` is odd, there is no constraint on `v` and `y` will have the same sign as `v`.) + +[[integer-of-enumeration,Operator Integer]] +Operator Integer:: ++ +[source,modelica] +---- +Integer(e) +---- ++ +Ordinal number of the expression `e` of enumeration type that evaluates to the enumeration value `E.enumvalue`, where `Integer(E.e1) = 1`, `Integer(E.en) = n`, for an enumeration type `E = enumeration(e1, ..., en)`. +See also <>. + +[[enumeration-of-integer, Operator ]] +Operator :: ++ +[source,modelica] +---- +EnumTypeName(i) +---- ++ +For any enumeration type `EnumTypeName`, returns the enumeration value `EnumTypeName.e` such that `Integer(EnumTypeName.e) = i`. +Refer to the definition of `Integer` above. ++ +It is an error to attempt to convert values of `i` that do not correspond to values of the enumeration type. +See also <>. + +[[to-string,Operator String]] +Operator String:: ++ +-- +[source,modelica] +---- +String(b, ) +String(i, ) +String(i, format = s) +String(r, ) +String(r, format = s) +String(e, ) +---- + +Convert a scalar non-`String` expression to a `String` representation. +The first argument may be a `Boolean` b, an `Integer` i, a `Real` r, or an enumeration value e (see <>). +The `` represent zero or more of the following named arguments (that cannot be passed as positional arguments): + +* `Integer minimumLength = 0`: Minimum length of the resulting string. If necessary, the blank character is used to fill up unused space. +* `Boolean leftJustified = true`: If true, the converted result is left justified in the string; if false it is right justified in the string. +* `Integer significantDigits = 6`: Number of significant digits in the result string. Only allowed when formatting a `Real` value. + +The standard type coercion described in <> shall not be applied for the first argument of `String`. +Hence, specifying `significantDigits` is an error when the first argument of `String` is an `Integer` expression. + +For `Real` expressions the output shall be according to the Modelica grammar. + +[NOTE] +Examples of `Real` values formatted with 6 significant digits: _12.3456_, _0.0123456_, _12345600_, _1.23456E-10_. + +The `format` string corresponding to `` is: + +* For `Real`: + + `(if leftJustified then "-" else "") + String(minimumLength) + "." + String(signficantDigits) + "g"` +* For `Integer`: + + `(if leftJustified then "-" else "") + String(minimumLength) + "d"` + +The ANSI-C style `format` string (which cannot be combined with any of the other named arguments) consists of a single conversion specification without the leading `%`. +It shall not contain a length modifier, and shall not use `*` for width and/or precision. +For both `Real` and `Integer` values, the conversion specifiers `f`, `e`, `E`, `g`, `G` are allowed. +For `Integer` values it is also allowed to use the `d`, `i`, `o`, `x`, `X`, `u`, and `c` conversion specifiers. +Using the `Integer` conversion specifiers for a `Real` value is a deprecated feature, where tools are expected to produce a result by either rounding the value, truncating the value, or picking one of the `Real` conversion specifiers instead. + +The `x`/`X` formats (hexa-decimal) and `c` (character) for `Integer` values give results that do not agree with the Modelica grammar. + +[example] +==== +Example: Some situations worth a remark: + +* `String(4.0, format = "g")` produces _4_ which is not a valid `Real` literal. However, it is an `Integer` literal that can be used almost anywhere in Modelica code instead of the `Real` literal `4.0` (with the first argument to `String` being a notable exception here). +* `String(4, format = ".3f")` uses the `Integer` case of `String` since no automatic type coerction takes place for the first argument. An implementation may internally convert the value to floating point and then fall back on the `Real` case implementation of `format = ".3f"`. +* `String(4611686018427387648, format = ".0f")` (a valid `Integer` value in an implementation with 64 bit `IntegerType`) may produce _4611686018427387904_ (not equal to input value), in case internal conversion to a 64 bit `double` is applied. +==== +-- + +==== Event Triggering Mathematical Functions + +The operators listed below trigger events if used outside of a `when`-clause and outside of a clocked discrete-time partition (see <>). + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`div(x, y)` |Division with truncation toward zero |<
> +|`mod(x, y)` |Integer modulus |<> +|`rem(x, y)` |Integer remainder |<> +|`ceil(x)` |Smallest integer `Real` not less than x |<> +|`floor(x)` |Largest integer `Real` not greater than x |<> +|`integer(x)` |Largest `Integer` not greater than x |<> +|=== + +These expression for `div`, `ceil`, `floor`, and `integer` are event generating expression. +The event generating expression for `mod(x,y)` is `floor(x/y)`, and for `rem(x,y)` it is `div(x,y)` -- i.e., events are not generated when `mod` or `rem` changes continuously in an interval, but when they change discontinuously from one interval to the next. + +[NOTE] +If this is not desired, the `noEvent` operator can be applied to them. +E.g., `noEvent(integer(v))`. + +[[div,Operator div]] +Operator div:: ++ +[source,modelica] +---- +div(x, y) +---- ++ +Algebraic quotient `x / y` with any fractional part discarded (also known as truncation toward zero). +Result and arguments shall have type `Real` or `Integer`. ++ +[NOTE] +This is defined for / in C99; in C89 the result for negative numbers is implementation-defined, so the standard function div must be used. ++ +Result and arguments shall have type `Real` or `Integer`. +If either of the arguments is `Real` the result is `Real` otherwise `Integer`. + +[[mod,Operator mod]] +Operator mod:: ++ +[source,modelica] +---- +mod(x, y) +---- ++ +Integer modulus of `x / y`, i.e., `mod(x, y) = x - floor(x / y) * y`. +Result and arguments shall have type `Real` or `Integer`. +If either of the arguments is `Real` the result is `Real` otherwise `Integer`. ++ +[NOTE] +Note, outside of a `when`-clause state events are triggered when the return value changes discontinuously. Examples: `mod(3, 1.4) = 0.2`, `mod(-3, 1.4) = 1.2`, `mod(3, -1.4) = -1.2`. + +[[rem,Operator rem]] +Operator rem:: ++ +[source,modelica] +---- +rem(x, y) +---- ++ +Integer remainder of `x / y`, such that `div(x, y) * y + rem(x, y) = x`. +Result and arguments shall have type `Real` or `Integer`. +If either of the arguments is `Real` the result is `Real` otherwise `Integer`. ++ +[NOTE] +Note, outside of a when-clause state events are triggered when the return value changes discontinuously. Examples: `rem(3, 1.4) = 0.2`, `rem(-3, 1.4) = -0.2`. + +[[ceil,Operator ceil]] +Operator ceil:: ++ +[source,modelica] +---- +ceil(x) +---- ++ +Smallest integer not less than `x`. +Result and argument shall have type `Real`. ++ +[NOTE] +Note, outside of a `when`-clause state events are triggered when the return value changes discontinuously. + +[[floor,Operator floor]] +Operator floor:: ++ +[source,modelica] +---- +floor(x) +---- ++ +Largest integer not greater than `x`. +Result and argument shall have type `Real`. ++ +[NOTE] +Note, outside of a `when`-clause state events are triggered when the return value changes discontinuously. + +[[integer,Operator integer]] +Operator integer:: ++ +[source,modelica] +---- +integer(x) +---- ++ +Largest integer not greater than `x`. +The argument shall have type `Real`. +The result has type `Integer`. ++ +[NOTE] +Note, outside of a `when`-clause state events are triggered when the return value changes discontinuously. + +==== Elementary Mathematical Functions + +The functions listed below are elementary mathematical functions. +Tools are expected to utilize well known properties of these functions (derivatives, inverses, etc) for symbolic processing of expressions and equations. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`sin(x)` |Sine | +|`cos(x)` |Cosine | +|`tan(x)` |Tangent (x shall not be: ..., -π/2, π/2, 3π/2, ...) | +|`asin(x)` |Inverse sine (-1 ≤ x ≤ 1) | +|`acos(x)` |Inverse cosine (-1 ≤ x ≤ 1) | +|`atan(x)` |Inverse tangent | +|`atan2(y, x)` |Principal value of the arc tangent of y/x |<> +|`sinh(x)` |Hyperbolic sine | +|`cosh(x)` |Hyperbolic cosine | +|`tanh(x)` |Hyperbolic tangent | +|`exp(x)` |Exponential, base e | +|`log(x)` |Natural (base e) logarithm (x > 0) | +|`log10(x)` |Base 10 logarithm (x > 0) | +|=== + +These functions are the only ones that can also be called using the deprecated `"builtin"` external language, see <>. + +[NOTE] +End user oriented information about the elementary mathematical functions can be found for the corresponding functions in the `Modelica.Math` package. + +[[atan2,Function atan2]] +Function atan2:: ++ +[source,modelica] +---- +atan2(y, x) +---- ++ +Principal value of the arc tangent of latexmath:[y/x], using the signs of the two arguments to determine the quadrant of the result. +The result latexmath:[\varphi] is in the interval latexmath:[\left[ -\pi, \pi \right\]] and satisfies: ++ +[latexmath] +++++ +\left|(x,\, y)\right|\, \cos(\varphi) = x\\ +\left|(x,\, y)\right|\, \sin(\varphi) = y +++++ + +==== Derivative and Special Purpose Operators with Function Syntax + +The operators listed below include the derivative operator and special purpose operators with function syntax. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`der(expr)` |Time derivative |<> +|`delay(expr, ...)` |Time delay |<> +|`cardinality(c)` |Number of occurrences in `connect`-equations |<> +|`homotopy(actual, simplified)` |Homotopy initialization |<> +|`semiLinear(x, k+, k-)` |Sign-dependent slope |<> +|`inStream(v)` |Stream variable flow into component |<> +|`actualStream(v)` |Actual value of stream variable |<> +|`spatialDistribution(...)` |Variable-speed transport |<> +|`getInstanceName()` |Name of instance at call site |<> +|=== + +The special purpose operators with function syntax where the call below uses named arguments can be called with named arguments (with the specified names), or with positional arguments (the inputs of the functions are in the order given in the calls below). + +[[operator:der,Operator der]] +Operator der:: ++ +-- +[source,modelica] +---- +der(expr) +---- +The time derivative of `expr`. +If the expression `expr` is a scalar it needs to be a subtype of `Real`. +The expression and all its time-varying subexpressions must be continuous and semi-differentiable. +An exception is when the operator `reinit` (see <>) is activated, as not even states are continuous. +If `expr` is an array, the operator is applied to all elements of the array. +For non-scalar arguments the function is vectorized according to <>. + +[NOTE] +For `Real` parameters and constants the result is a zero scalar or array of the same size as the variable. + +[example] +==== +Example: For continuous expression we have several cases. + +[source,modelica] +---- + Real when1, x1, x2, x3, w1, y1, y2, y3; +equation + when sample(1, 1) then + when1 = ...; + end when; + x1 = if time>=0 then 0 else time; // Continuous + x2 = if time<1 then 0 else time; // Not continuous + x3 = x1 * x2; // The product is continuous, despite x2 + + w1 = der(when1); // Explicitly illegal + y1 = der(x1); // Ok + y2 = der(x2); // Not ok at time=1 + y3 = der(x3); // Ok +---- + +Since it is difficult to prove whether expressions are continuous, tools can dynamically check the continuity such that `x2` only generates an error when the integration reaches the discontinuity. +==== + +[example] +==== +Example: Exception for reinit: + +[source,modelica] +---- + Real x4, x5, x6; +equation + when sample(1, 1) then + reinit(x4, 0); + end when; + der(x4) = time; + x5 = x4^2; + x6 = der(x5); // Allowed even if x5 is not continuous, since that is due to reinit +---- +==== +-- + +[[operator:delay,Operator delay]] +Operator delay:: ++ +[source,modelica] +---- +delay(expr, delayTime, delayMax) +delay(expr, delayTime) +---- +Evaluates to `expr(time - delayTime)` for `time > time.start + delayTime` and `expr(time.start)` for `time <= time.start + delayTime`. +The arguments, i.e., `expr`, `delayTime` and `delayMax`, need to be subtypes of `Real`. +`delayMax` needs to be additionally a parameter expression. +The following relation shall hold: `0 <= delayTime <= delayMax`, otherwise an error occurs. +If `delayMax` is not supplied in the argument list, `delayTime` needs to be a parameter expression. +For non-scalar arguments the function is vectorized according to <>. +For further details, see <<>>. + + +[[operator:cardinality,Operator cardinality]] +Operator cardinality:: ++ +[source,modelica] +---- +cardinality(c) +---- +[NOTE] +This is a deprecated operator. +It should no longer be used, since it will be removed in one of the next Modelica releases. + +Returns the number of (inside and outside) occurrences of connector instance `c` in a `connect`-equation as an `Integer` number. +For further details, see <>. + +[[operator:homotopy,Operator homotopy]] +Operator homotopy:: ++ +[source,modelica] +---- +homotopy(actual = actual, simplified = simplified) +---- +The scalar expressions `actual` and `simplified` are subtypes of `Real`. +A Modelica translator should map this operator into either of the two forms: + +. Returns `actual` (trivial implementation). +. In order to solve algebraic systems of equations, the operator might during the solution process return a combination of the two arguments, ending at actual. ++ +.Example +`actual * λ + simplified * (1 - λ)`, where λ is a homotopy parameter going from 0 to 1. ++ +The solution must fulfill the equations for `homotopy` returning `actual`. + +For non-scalar arguments the function is vectorized according to <>. + +[[operator:semiLinear,Operator semiLinear]] +Operator semiLinear:: ++ +[source,modelica] +---- +semiLinear(x, k+, k-) +---- +Returns: `smooth(0, if x >= 0 then k+ * x else k- * x)`. +The result is of type `Real`. +For non-scalar arguments the function is vectorized according to <>. + +[[operator:inStream,Operator inStream]] +Operator inStream:: ++ +[source,modelica] +---- +inStream(v) +---- +`inStream(v)` is only allowed for stream variables v defined in stream connectors, and is the value of the stream variable v close to the connection point assuming that the flow is from the connection point into the component. +This value is computed from the stream connection equations of the flow variables and of the stream variables. +The operator is vectorizable. + +[[operator:actualStream,Operator actualStream]] +Operator actualStream:: ++ +[source,modelica] +---- +actualStream(v) +---- +`actualStream(v)` returns the actual value of the stream variable v for any flow direction. +The operator is vectorizable. + +[[operator:spatialDistribution,Operator spatialDistributioner]] +Operator spatialDistribution:: ++ +[source,modelica] +---- +spatialDistribution( + in0 = in0, in1 = in1, x = x, + positiveVelocity = ..., + initialPoints = ..., + initialValues = ...) +---- +`spatialDistribution` allows approximation of variable-speed transport of properties. + +[[operator:getInstanceName,Operator getInstanceName]] +Operator getInstanceName:: ++ +[source,modelica] +---- +getInstanceName() +---- +Returns a string with the name of the model/block that is simulated, appended with the fully qualified name of the instance in which this function is called. + +===== delay +:id: delay + +[NOTE] +==== +`delay` allows a numerical sound implementation by interpolating in the (internal) integrator polynomials, as well as a more simple realization by interpolating linearly in a buffer containing past values of expression _expr_. +Without further information, the complete time history of the delayed signals needs to be stored, because the delay time may change during simulation. +To avoid excessive storage requirements and to enhance efficiency, the maximum allowed delay time has to be given via _delayMax_. +This gives an upper bound on the values of the delayed signals which have to be stored. +For real-time simulation where fixed step size integrators are used, this information is sufficient to allocate the necessary storage for the internal buffer before the simulation starts. +For variable step size integrators, the buffer size is dynamic during integration. + +In principle, `delay` could break algebraic loops. +For simplicity, this is not supported because the minimum delay time has to be given as additional argument to be fixed at compile time. +Furthermore, the maximum step size of the integrator is limited by this minimum delay time in order to avoid extrapolation in the delay buffer. +==== + +===== spatialDistribution +:id: spatialdistribution + +[NOTE] +==== +Many applications involve the modelling of variable-speed transport of properties. +One option to model this infinite-dimensional system is to approximate it by an ODE, but this requires a large number of state variables and might introduce either numerical diffusion or numerical oscillations. +Another option is to use a built-in operator that keeps track of the spatial distribution of z(ξ, t), by suitable sampling, interpolation, and shifting of the stored distribution. +In this case, the internal state of the operator is hidden from the ODE solver. +==== + +`spatialDistribution` allows the infinite-dimensional problem below to be solved efficiently with good accuracy + +[stem] +++++ +\frac{\partial z(\xi, t)}{\partial t} + v(t) \frac{\partial z(\xi, t)}{\partial \xi} = 0\\ +z(0, t) = \mathrm{in}_{0}(t) \quad \text{if $v(t) \geq 0$}\\ +z(1, t) = \mathrm{in}_{1}(t) \quad \text{if $v(t) < 0$} +++++ + +where z(ξ, t) is the transported quantity, ξ is the normalized spatial coordinate (0 ≤ ξ ≤ 1), t is the time, v(t) is the normalized transport velocity and the boundary conditions are set at either ξ = 0 or ξ = 1, depending on the sign of the velocity. + +The calling syntax is: + +[source,modelica] +---- +(out0, out1) = spatialDistribution(in0, in1, x, positiveVelocity, + initialPoints = {0.0, 1.0}, + initialValues = {0.0, 0.0}); +---- + +where `in0`, `in1`, `out0`, `out1`, and `x` are all subtypes of `Real`, `positiveVelocity` is a `Boolean`, and `initialPoints` and `initialValues` are arrays of subtypes of `Real`. +The position `x` is the integral of the transport velocity v, where the constant of integration does not matter. +The arrays `initialPoints` and `initialValues` shall be parameter expressions of equal size, containing the ξ coordinates and the z values of a finite set of points describing the initial distribution of z(ξ, t₀). +The `out0` and `out1` are given by the solutions at z(0, t) and z(1, t); and `in0` and `in1` are the boundary conditions at z(0, t) and z(1, t) (at each point in time only one of `in0` and `in1` is used). +The `initialPoints` array shall span the entire range from 0 to 1, and must be sorted in non-descending order. +The operator can not be vectorized according to the vectorization rules described in <>. +The operator can be vectorized only with respect to the arguments `in0` and `in1` (which must have the same size), returning vectorized outputs `out0` and `out1` of the same size; the arguments `initialPoints` and `initialValues` are vectorized accordingly. + +The solution, z, can be described in terms of characteristics: + +[stem] +++++ +z(\xi+\int_{t}^{t+\beta}\! v(\alpha) \mathrm{d}\alpha,\, t+\beta) = z(\xi, t), \quad\text{for all $\beta$ as long as staying inside the domain} +++++ + +This allows the direct computation of the solution based on interpolating the boundary conditions. + +`spatialDistribution` can be described in terms of the pseudo-code given as a block: + +[source,modelica] +---- +block spatialDistribution + input Real in0; + input Real in1; + input Real x; + input Boolean positiveVelocity; + parameter Real initialPoints(each min=0, each max=1)[:] = {0.0, 1.0}; + parameter Real initialValues[:] = {0.0, 0.0}; + output Real out0; + output Real out1; +protected + Real points[:]; + Real values[:]; + Real x0; + Integer m; +algorithm + /* The notation + * x y + * is used below as a shorthand for + * if x then y else false + * also known as "short-circuit evaluation of x and y". + */ + if positiveVelocity then + out1 := interpolate(points, values, 1 - (x - x0)); + out0 := values[1]; // Similar to in0 but avoiding algebraic loop. + else + out0 := interpolate(points, values, 0 - (x - x0)); + out1 := values[end]; // Similar to in1 but avoiding algebraic loop. + end if; + when then + if x > x0 then + m := size(points, 1); + while m > 0 points[m] + (x - x0) >= 1 loop + m := m - 1; + end while; + values := cat(1, + {in0}, + values[1:m], + {interpolate(points, values, 1 - (x - x0))}); + points := cat(1, {0}, points[1:m] .+ (x-x0), {1}); + elseif x < x0 then + m := 1; + while m < size(points, 1) points[m] + (x - x0) <= 0 loop + m := m + 1; + end while; + values := cat(1, + {interpolate(points, values, 0 - (x - x0))}, + values[m:end], + {in1}); + points := cat(1, {0}, points[m:end] .+ (x - x0), {1}); + end if; + x0 := x; + end when; +initial algorithm + x0 := x; + points := initialPoints; + values := initialValues; +end spatialDistribution; +---- + +[NOTE] +==== +Note that the implementation has an internal state and thus cannot be described as a function in Modelica; `initialPoints` and `initialValues` are declared as parameters to indicate that they are only used during initialization. + +The infinite-dimensional problem stated above can then be formulated in the following way: + +[source,modelica] +---- +der(x) = v; +(out0, out1) = spatialDistribution(in0, in1, x, v >= 0, + initialPoints, initialValues); +---- + +Events are generated at the exact instants when the velocity changes sign -- if this is not needed, `noEvent` can be used to suppress event generation. + +If the velocity is known to be always positive, then `out0` can be omitted, e.g.: + +[source,modelica] +---- +der(x) = v; +(, out1) = spatialDistribution(in0, 0, x, true, initialPoints, initialValues); +---- + +Technically relevant use cases for the use of `spatialDistribution` are modeling of electrical transmission lines, pipelines and pipeline networks for gas, water and district heating, sprinkler systems, impulse propagation in elongated bodies, conveyor belts, and hydraulic systems. +Vectorization is needed for pipelines where more than one quantity is transported with velocity `v` in the example above. +==== + +===== cardinality (deprecated) +:id: cardinality-deprecated + +[NOTE] +==== +`cardinality` is deprecated for the following reasons and will be removed in a future release: + +* Reflective operator may make early type checking more difficult. +* Almost always abused in strange ways +* Not used for Bond graphs even though it was originally introduced for that purpose. +==== + +[NOTE] +==== +`cardinality` allows the definition of connection dependent equations in a model, for example: + +[source,modelica] +---- +connector Pin + Real v; + flow Real i; +end Pin; +model Resistor + Pin p, n; +equation + assert(cardinality(p) > 0 and cardinality(n) > 0, + "Connectors p and n of Resistor must be connected"); + // Equations of resistor + ... +end Resistor; +---- +==== + +The cardinality is counted after removing conditional components, and shall not be applied to expandable connectors, elements in expandable connectors, or to arrays of connectors (but can be applied to the scalar elements of array of connectors). +`cardinality` should only be used in the condition of assert and `if`-statements that do not contain `connect` and similar operators, see <>. + +===== homotopy +:id: homotopy + +[NOTE] +==== +During the initialization phase of a dynamic simulation problem, it often happens that large nonlinear systems of equations must be solved by means of an iterative solver. +The convergence of such solvers critically depends on the choice of initial guesses for the unknown variables. +The process can be made more robust by providing an alternative, simplified version of the model, such that convergence is possible even without accurate initial guess values, and then by continuously transforming the simplified model into the actual model. +This transformation can be formulated using expressions of this kind: + +[stem] +++++ +\lambda\cdot\text{actual} + (1-\lambda)\cdot\text{simplified} +++++ + +in the formulation of the system equations, and is usually called a homotopy transformation. +If the simplified expression is chosen carefully, the solution of the problem changes continuously with λ, so by taking small enough steps it is possible to eventually obtain the solution of the actual problem. + +The operator can be called with ordered arguments or preferably with named arguments for improved readability. + +It is recommended to perform (conceptually) one homotopy iteration over the whole model, and not several homotopy iterations over the respective non-linear algebraic equation systems. +The reason is that the following structure can be present: + +[source,modelica] +---- +w = f₁(x) // has homotopy +0 = f₂(der(x), x, z, w) +---- + +Here, a non-linear equation system f₂ is present. +`homotopy` is, however used on a variable that is an "input" to the non-linear algebraic equation system, and modifies the characteristics of the non-linear algebraic equation system. +The only useful way is to perform the homotopy iteration over f₁ and f₂ together. + +The suggested approach is "conceptual", because more efficient implementations are possible, e.g., by determining the smallest iteration loop, that contains the equations of the first BLT block in which `homotopy` is present and all equations up to the last BLT block that describes a non-linear algebraic equation system. + +A trivial implementation of `homotopy` is obtained by defining the following function in the global scope: + +[source,modelica] +---- +function homotopy + input Real actual; + input Real simplified; + output Real y; +algorithm + y := actual; + annotation(Inline = true); +end homotopy; +---- +==== + +[example] +==== +Example 1: In electrical systems it is often difficult to solve non-linear algebraic equations if switches are part of the algebraic loop. +An idealized diode model might be implemented in the following way, by starting with a "flat" diode characteristic and then move with `homotopy` to the desired "steep" characteristic: + +[source,modelica] +---- +model IdealDiode + ... + parameter Real Goff = 1e-5; +protected + Real Goff_flat = max(0.01, Goff); + Real Goff2; +equation + off = s < 0; + Goff2 = homotopy(actual = Goff, simplified = Goff_flat); + u = s * (if off then 1 else Ron2) + Vknee; + i = s * (if off then Goff2 else 1 ) + Goff2*Vknee; + ... +end IdealDiode; +---- +==== + +[example] +==== +Example 2: In electrical systems it is often useful that all voltage sources start with zero voltage and all current sources with zero current, since steady state initialization with zero sources can be easily obtained. +A typical voltage source would then be defined as: + +[source,modelica] +---- +model ConstantVoltageSource + extends Modelica.Electrical.Analog.Interfaces.OnePort; + parameter Modelica.Units.SI.Voltage V; +equation + v = homotopy(actual = V, simplified = 0.0); +end ConstantVoltageSource; +---- +==== + +[example] +==== +Example 3: In fluid system modelling, the pressure/flowrate relationships are highly nonlinear due to the quadratic terms and due to the dependency on fluid properties. +A simplified linear model, tuned on the nominal operating point, can be used to make the overall model less nonlinear and thus easier to solve without accurate start values. +Named arguments are used here in order to further improve the readability. + +[source,modelica] +---- +model PressureLoss + import Modelica.Units.SI; + ... + parameter SI.MassFlowRate m_flow_nominal "Nominal mass flow rate"; + parameter SI.Pressure dp_nominal "Nominal pressure drop"; + SI.Density rho "Upstream density"; + SI.DynamicViscosity lambda "Upstream viscosity"; +equation + ... + m_flow = homotopy(actual = turbulentFlow_dp(dp, rho, lambda), + simplified = dp/dp_nominal*m_flow_nominal); + ... +end PressureLoss; +---- +==== + +[example] +==== +Example 4: Note that `homotopy` _shall not_ be used to combine unrelated expressions, since this can generate singular systems from combining two well-defined systems. + +[source,modelica] +---- +model DoNotUse + Real x; + parameter Real x0 = 0; +equation + der(x) = 1-x; +initial equation + 0 = homotopy(der(x), x - x0); +end DoNotUse; +---- + +The initial equation is expanded into + +[stem] +++++ +0 = \lambda*\mathrm{der}(x)+(1-\lambda)(x-x_0) +++++ + +and you can solve the two equations to give + +[stem] +++++ +x = \frac{\lambda+(\lambda-1)x_0}{2\lambda-1} +++++ + +which has the correct value of x₀ at λ = 0 and of 1 at λ = 1, but unfortunately has a singularity at λ = 0.5. +==== + +===== semiLinear +:id: semilinear + +(See definition of `semiLinear` in <>). +In some situations, equations with `semiLinear` become underdetermined if the first argument (`x`) becomes zero, i.e., there are an infinite number of solutions. +It is recommended that the following rules are used to transform the equations during the translation phase in order to select one meaningful solution in such cases: + +* The equations ++ +[source,modelica] +---- +y = semiLinear(x, sa, s1); +y = semiLinear(x, s1, s2); +y = semiLinear(x, s2, s3); +... +y = semiLinear(x, sN, sb); +... +---- ++ +may be replaced by ++ +[source,modelica] +---- +s1 = if x >= 0 then sa else sb +s2 = s1; +s3 = s2; +... +sN = sN-1; +y = semiLinear(x, sa, sb); +---- + +* The equations ++ +[source,modelica] +---- +x = 0; +y = 0; +y = semiLinear(x, sa, sb); +---- ++ +may be replaced by ++ +[source,modelica] +---- +x = 0 +y = 0; +sa = sb; +---- + +[NOTE] +==== +For symbolic transformations, the following property is useful (this follows from the definition): + +[source,modelica] +---- +semiLinear(m_flow, port_h, h); +---- + +is identical to: + +[source,modelica] +---- +-semiLinear(-m_flow, h, port_h); +---- + +The `semiLinear` function is designed to handle reversing flow in fluid systems, such as + +[source,modelica] +---- +H_flow = semiLinear(m_flow, port.h, h); +---- + +i.e., the enthalpy flow rate `H_flow` is computed from the mass flow rate `m_flow` and the upstream specific enthalpy depending on the flow direction. +==== + +===== getInstanceName +:id: getinstancename + +Returns a string with the name of the model/block that is simulated, appended with the fully qualified name of the instance in which this function is called. + +[example] +==== +Example: + +[source,modelica] +---- +package MyLib + model Vehicle + Engine engine; + ... + end Vehicle; + model Engine + Controller controller; + ... + end Engine; + model Controller + equation + Modelica.Utilities.Streams.print("Info from: " + getInstanceName()); + end Controller; +end MyLib; +---- + +If `MyLib.Vehicle` is simulated, the call of `getInstanceName()` returns `"Vehicle.engine.controller"`. +==== + +If this function is not called inside a model or block (e.g., the function is called in a function or in a constant of a package), the return value is not specified. + +The simulation result should not depend on the return value of this function. + +==== Event-Related Operators with Function Syntax + +The operators listed below are event-related operators with function syntax. +The operators `noEvent`, `pre`, `edge`, and `change`, are vectorizable according to <>. + +[cols="a,a,a",options=autowidth] +|=== +|Expression |Description |Details + +|`initial()` |Predicate for the initialization phase |<> +|`terminal()` |Predicate for the end of a successful analysis |<> +|`noEvent(expr)` |Evaluate expr without triggering events |<> +|`smooth(p, expr)` |Treat expr as p times continuously differentiable |<> +|`sample(start, interval)` |Periodic triggering of events |<> +|`pre(y)` |Left limit y(t-) of variable y(t) |<> +|`edge(b)` |Expands into `(b and not pre(b))` |<> +|`change(v)` |Expands into `(v <> pre(v))` |<> +|`reinit(x, expr)` |Reinitialize x with expr |<> +|=== + +[[operator:initial,Operator initial]] +Operator initial:: ++ +-- +[source,modelica] +---- +initial() +---- + +Returns `true` during the initialization phase and `false` otherwise. + +[NOTE] +Hereby, `initial()` triggers a time event at the beginning of a simulation. +-- + +[[operator:terminal,Operator terminal]] +Operator terminal:: ++ +-- +[source,modelica] +---- +terminal() +---- +Returns `true` at the end of a successful analysis. + +[NOTE] +Hereby, `terminal()` ensures an event at the end of successful simulation. +-- + +[[operator:noEvent,Operator noEvent]] +Operator noEvent:: ++ +-- +[source,modelica] +---- +noEvent(expr) +---- + +`Real` elementary relations within expr are taken literally, i.e., no state or time event is triggered. +No zero crossing functions shall be used to monitor any of the normally event-generating subexpressions inside _expr_. +Inside functions, `noEvent` only makes a difference in combination with the function annotation `GenerateEvents = true` (see <>). +See also <> and <>. +-- + +[[operator:smooth,Operator smooth]] +Operator smooth:: ++ +-- +[source,modelica] +---- +smooth(p, expr) +---- + +If latexmath:[p \geq 0] `smooth(p, expr)` returns _expr_ and states that _expr_ is `p` times continuously differentiable, i.e., _expr_ is continuous in all `Real` variables appearing in the expression and all partial derivatives with respect to all appearing real variables exist and are continuous up to order `p`. +The argument `p` should be a scalar `Integer` parameter expression. +The only allowed types for _expr_ in `smooth` are: `Real` expressions, arrays of allowed expressions, and records containing only components of allowed expressions. + +`smooth` should be used instead of `noEvent` in order to avoid events for efficiency reasons. +A tool is free to not generate events for expressions inside `smooth`. +However, `smooth` does not guarantee that no events will be generated, and thus it can be necessary to use `noEvent` inside `smooth`. + +[NOTE] +Note that `smooth` does not guarantee a smooth output if any of the occurring variables change discontinuously. + +[example] +==== +Example: + +[source,modelica] +---- + Real x, y, z; +equation + x = if time < 1 then 2 else time - 2; + z = smooth(0, if time < 0 then 0 else time); + y = smooth(1, + noEvent(if x < 0 then 0 else sqrt(x) * x)); // Needs noEvent. +---- +==== +-- + +[[operator:sample,Operator sample]] +Operator sample:: ++ +-- +[source,modelica] +---- +sample(start, interval) +---- +Returns `true` and triggers time events at time instants `start + i * interval` for i = 0, 1, ... , and is only true during the first event iteration at those times. +At event iterations after the first one at each event and during continuous integration the operator always returns `false`. +The starting time `start` and the sample interval `interval` must be parameter expressions and need to be a subtype of `Real` or `Integer`. +The sample interval `interval` must be a positive number. +-- + +[[operator:pre,Operator pre]] +Operator pre:: ++ +-- +[source,modelica] +---- +pre(y) +---- + +Returns the left limit _y(t^-^)_ of variable _y(t)_ at a time instant t. +At an event instant, _y(t^-^)_ is the value of _y_ after the last event iteration at time instant _t_ (see comment below). +Any subscripts in the component expression _y_ must be parameter expressions. +`pre` can be applied if the following three conditions are fulfilled simultaneously: (a) variable _y_ is either a subtype of a simple type or is a record component, (b) _y_ is a discrete-time expression (c) the operator is not applied in a `function` class. + +[NOTE] +This can be applied to continuous-time variables in `when`-clauses, see <> for the definition of discrete-time expression. + +The first value of `pre(y)` is determined in the initialization phase. + +A new event is triggered if there is at least for one variable `v` such that `pre(v) <> v` after the active model equations are evaluated at an event instant. +In this case the model is at once reevaluated. +This evaluation sequence is called *event iteration*. +The integration is restarted once `pre(v) == v` for all `v` appearing inside `pre(...)`. + +[NOTE] +==== +If `v` and `pre(v)` are only used in `when`-clauses, the translator might mask event iteration for variable `v` since `v` cannot change during event iteration. +It is a quality of implementation to find the minimal loops for event iteration, i.e., not all parts of the model need to be reevaluated. + +The language allows mixed algebraic systems of equations where the unknown variables are of type `Real`, `Integer`, `Boolean`, or an enumeration. +These systems of equations can be solved by a global fix point iteration scheme, similarly to the event iteration, by fixing the `Boolean`, `Integer`, and/or enumeration unknowns during one iteration. +Again, it is a quality of implementation to solve these systems more efficiently, e.g., by applying the fix point iteration scheme to a subset of the model equations. +==== +-- + +[[operator:edge,Operator edge]] +Operator edge:: ++ +[source,modelica] +---- +edge(b) +---- ++ +Expands into `(b and not pre(b))` for Boolean variable b. +The same restrictions as for `pre` apply (e.g., not to be used in `function` classes). + +[[operator:change,Operator change]] +Operator change:: ++ +-- +[source,modelica] +---- +change(v) +---- +Expands into `(v <> pre(v))`. +The same restrictions as for `pre` apply. +-- + +[[operator:reinit,Operator reinit]] +Operator reinit:: ++ +-- + +[source,modelica] +---- +reinit(x, expr) +---- + +In the body of a `when`-clause, reinitializes x with expr at an event instant. +x is a scalar or array `Real` variable that is implicitly defined to have `StateSelect.always`. + +[NOTE] +It is an error if the variable cannot be selected as a state. + +`expr` needs to be type-compatible with `x`. +`reinit` can only be applied once for the same variable -- either as an individual variable or as part of an array of variables. +It can only be applied in the body of a `when`-clause in an equation section. +See also <>. +-- + +=== Variability of Expressions + +The concept of variability of an expression indicates to what extent the expression can vary over time. +See also <> regarding the concept of variability. + +There are four levels of variability of expressions, starting from the least variable: + +* constant variability +* parameter variability +* discrete-time variability +* continuous-time variability + +While many invalid models can be rejected based on the declared variabilities of variables alone (without the concept of expression variability), the following rules both help enforcing compliance of computed solutions to declared variability, and impose additional restrictions that simplify reasoning and reporting of errors: + +* For an assignment `v := expr` or binding equation `v = expr`, v must be declared to be at least as variable as `expr`. +* For multiple return assignment `(x1, ..., xn) := expr` (see <>), all of x1, ..., xn must be declared to be at least as variable as `expr`. +* When determining whether an equation can contribute to solving for a variable v (for instance, when applying the perfect matching rule, see <>), the equation can only be considered contributing if the resulting solution would be at most as variable as `v`. + +[example] +==== +Example: The (underdetermined) model `Test` below illustrates two kinds of consequences due to variability constraints. +First, it contains variability errors for declaration equations and assignments. +Second, it illustrates the impact of variability on the matching of equations to variables, which can lead to violation of the perfect matching rule. +Details of how variabilities are determined are given in the sections below. +The discrete-valued equation variability rule mentioned in the comments below refer to the rule in <> that requires both sides of the Boolean equation to be discrete-time. + +[source,modelica] +---- +model Constants + parameter Real p1 = 1; + constant Real c1 = p1 + 2; // Error, not a constant expression. + parameter Real p2 = p1 + 2; // Fine. +end Constants; +model Test + Constants c1(p1 = 3); // Fine. + Constants c2(p2 = 7); // Fine, declaration equation can be modified. + Real x; + Boolean b1 = noEvent(x > 1); // Error, since b1 is a discrete-time variable + // and noEvent(x > 1) is not discrete-time. + Boolean b2; + Integer i1; + Integer i2; +algorithm + i1 := x; // Error, assignment to variable of lesser variability. +equation + /* The equation below can be rejected for two reasons: + * 1. Discrete-valued equation variability rule requires both sides to be + * discrete-time. + * 2. It violates the perfect matching rule, as no variable can be solved + * with correct variability using this equation. + */ + b2 = noEvent(x > 1); // Error, see above. + i2 = x; // No variability error, and can be matched to x. +end Test; +---- +==== + +==== Function Variability + +The variability of function calls needs to consider both the variability of arguments directly given in the function and the variability of the used default arguments, if any. +This is especially a concern for functions given as a short class, see <>. +This has additional implications for redeclarations, see <>. +The purity of the function, see <>, does not influence the variability of the function call. + +[NOTE] +The restrictions for calling functions declared as `impure` serve a similar purpose as the variability restrictions, see <>, and thus it is not necessary to consider purity in the definition of variability. +Consider a function reading an external file and returning some value from that file. +Different uses can have the file updated before the simulation (as a parameter-expression), or during the simulation (as a discrete-time expression). +Thus it depends on the use case and the specific file, not the function itself, and it would even be possible to update the file in continuous time (as part of an algorithm) and still use the same function. + +==== Constant Expressions + +Constant expressions are: + +* `Real`, `Integer`, `Boolean`, `String`, and `enumeration` literals. +* Constant variables, see <>. +* Except for the special built-in operators `initial`, `terminal`, `der`, `edge`, `change`, `sample`, and `pre`, a function or operator with constant subexpressions as argument (and no parameters defined in the function) is a constant expression. +* Some function calls are constant expressions regardless of the arguments: +** `ndims(A)` + +==== Evaluable Expressions + +Evaluable expressions are: + +* Constant expressions. +* Evaluable parameter variables, see <>. +* Input variables in functions behave as though they were evaluable expressions. +* Except for the special built-in operators `initial`, `terminal`, `der`, `edge`, `change`, `sample`, and `pre`, a function or operator with evaluable subexpressions is an evaluable expression. +* The sub-expression `end` used in `A[... end ...]` if `A` is a variable declared in a non-`function` class. +* Some function calls are evaluable expressions even if the arguments are not: +** `cardinality(c)`, see restrictions for use in <>. +** `size(A)` (including `size(A, j)` where `j` is an evaluable expression) if `A` is variable declared in a non-function class. +** `Connections.isRoot(A.R)` +** `Connections.rooted(A.R)` + +==== Parameter Expressions + +Parameter expressions are: + +* Evaluable expressions. +* Non-evaluable parameter variables, see <>. +* Except for the special built-in operators `initial`, `terminal`, `der`, `edge`, `change`, `sample`, and `pre`, a function or operator with parameter subexpressions is a parameter expression. +* Some function calls are parameter expressions even if the arguments are not: +** `size(A, j)` where `j` is a parameter expression, if `A` is variable declared in a non-function class. + +==== Discrete-Time Expressions + +Discrete-time expressions are: + +* Parameter expressions. + +* Discrete-time variables, see <>. + +* Function calls where all input arguments of the function are discrete-time expressions. + +* Expressions where all the subexpressions are discrete-time expressions. + +* Expressions in the body of a `when`-clause, `initial equation`, or `initial algorithm`. + +* Expressions in a clocked discrete-time partition, see <>. + +* Unless inside `noEvent`: Ordered relations (`>`, `<`, `>=`, `+<=+`) and the event generating functions `ceil`, `floor`, `div`, and `integer`, if at least one argument is non-discrete-time expression and subtype of `Real`. ++ +[NOTE] +These will generate events, see <>. +Note that `rem` and `mod` generate events but are not discrete-time expressions. +In other words, relations inside `noEvent`, such as `noEvent(x>1)`, are not discrete-time expressions. + +* The functions `pre`, `edge`, and `change` result in discrete-time expressions. + +* Expressions in functions behave as though they were discrete-time expressions. + +Inside an `if`-expression, `if`-clause, `while`-statement or `for`-clause, that is controlled by a non-discrete-time (that is continuous-time, but not discrete-time) switching expression and not in the body of a `when`-clause, it is not legal to have assignments to discrete-time variables, equations between discrete-time expressions, or real elementary relations/functions that should generate events. + +[NOTE] +The restriction above is necessary in order to guarantee that all equations for discrete-time variable are discrete-time expressions, and to ensure that crossing functions do not become active between events. + +For a scalar or array equation `expr1 = expr2` where neither expression is of base type `Real`, both expressions must be discrete-time expressions. +For a record equation, the rule applies recursively to each of the components of the record. +This is called the _discrete-valued equation variability rule_. + +[NOTE] +==== +For a scalar equation, the rule follows from the observation that a discrete-valued equation does not provide sufficient information to solve for a continuous-valued variable. +Hence, and according to the perfect matching rule (see <>), such an equation must be used to solve for a discrete-valued variable. +By the interpretation of <> in <>, it follows that one of `expr1` and `expr2` must be the variable, and the other expression its solution. +Since a discrete-valued variable is a discrete-time expression, it follows that its solution on the other side of the equation must have at most discrete-time variability. +That is, both sides of the equation are discrete-time expressions. + +For example, this rule shows that (outside of a `when`-clause) `noEvent` cannot be applied to either side of a `Boolean`, `Integer`, `String`, or `enumeration` equation, as this would result in a non-discrete-time expression. + +For an array equation, note that each array can have only one element type, so if one element is `Real`, then all other entries must also be `Real`, possibly making use of standard type coercion, <>. +Hence, if the base type is not `Real`, all elements of the array are discrete-valued, allowing the argument above for a scalar equation to be applied elementwise to the array equation. +That is, all array elements on both sides of the array equation will have discrete-time variability, showing that also the entire arrays `expr1` and `expr2` are discrete-time expressions. + +For a record equation, the components of the record have independent types, and the equation is seen as a collection of equations for the individual components of the record. +In order to support records with components of mixed variability, a record equation with sides given by either record variables or record constructors is conceptually split before variability is determined. +==== + +[example] +==== +Example: Discrete-valued equation variability rule applied to record equations. +In the first of the equations below, having a record constructor on both sides of the equation, the equation is conceptually split, and variabilities of `time` and `true` are considered separately. +In the second equation, the `makeR` function call -- regardless of inlining -- means that the equation cannot be conceptually split into individual components of the record. +The variability of the `makeR` call is continuous-time due to the `time` argument, which also becomes the variability of the `b` member of the call. + +[source,modelica] +---- +record R + Real x; + Boolean b; +end R; + +function makeR "Function wrapper around record constructor" + input Real xx; + input Boolean bb; + output R r = R(xx, bb); + annotation(Inline = true); // Inlining doesn't help. +end makeR; + +model Test + R r1, r2; +equation + r1 = R(time, true); // OK: Discrete-time Boolean member. + r2 = makeR(time, true); // Error: Continuous-time Boolean member. +end Test; +---- +==== + +==== Continuous-Time and Non-Discrete-Time Expressions + +All expressions are continuous-time expressions including constant, parameter and discrete-time expressions. +The term _non-discrete-time expression_ refers to expressions that are neither constant, parameter nor discrete-time expressions. +For example, `time` is a continuous-time built-in variable (see <>) and `time + 1` is a non-discrete-time expression. +Note that plain `time` may, depending on context, refer to the continuous-time variable or the non-discrete-time expression. \ No newline at end of file diff --git a/docs/4___classes.adoc b/docs/4___classes.adoc new file mode 100644 index 000000000..786d107fb --- /dev/null +++ b/docs/4___classes.adoc @@ -0,0 +1,1922 @@ +== Classes, Predefined Types, and Declarations +:id: class-predefined-types-and-declarations + +The fundamental structuring unit of modeling in Modelica is the class. +Classes provide the structure for objects, also known as instances. +Classes can contain equations which provide the basis for the executable code that is used for computation in Modelica. +Conventional algorithmic code can also be part of classes. +All data objects in Modelica are instantiated from classes, including the basic data types -- `Real`, `Integer`, `String`, `Boolean` -- and enumeration types, which are built-in classes or class schemata. + +Declarations are the syntactic constructs needed to introduce classes and objects (i.e., components). + +=== Access Control -- Public and Protected Elements + +Members of a Modelica class can have two levels of visibility: `public` or `protected`. +The default is `public` if nothing else is specified. + +A protected element, `P`, in classes and components shall not be accessed via dot notation (e.g., `A.P`, `a.P`, `a[1].P`, `a.b.P`, `.A.P`; but there is no restriction on using `P` or `P.x` for a protected element `P`). +They shall not be modified or redeclared except for modifiers applied to protected elements in a base class modification (not inside any component or class) and the modifier on the declaration of the protected element. + +[example] +==== +Example: + +[source,modelica] +---- +package A + model B + protected + parameter Real x; + end B; +protected + model C end C; +public + model D + C c; // Legal use of protected class C from enclosing scope + extends A.B(x=2); // Legal modifier for x in derived class + // also x.start=2 and x(start=2) are legal. + Real y=x; // Legal use of x in derived class + end D; + model E + A.B a(x=2); // Illegal modifier, also x.start=2 and x(start=2) are illegal + A.C c; // Illegal use of protected class C + model F=A.C; // Illegal use of protected class C + end E; +end A; +---- +==== + +All elements defined under the heading `protected` are regarded as protected. +All other elements (i.e., defined under the heading `public`, without headings or in a separate file) are public (i.e., not protected). +Regarding inheritance of protected and public elements, see <>. + +=== Double Declaration not Allowed + +The name of a declared element shall not have the same name as any other element in its partially flattened enclosing class. +However, the internal flattening of a class can in some cases be interpreted as having two elements with the same name; these cases are described in <> and <>. + +[example] +==== +Example: + +[source,modelica] +---- +record R + Real x; +end R; +model M // wrong Modelica model + R R; // not correct, since component name and type specifier are identical +equation + R.x = 0; +end M; +---- +==== + +=== Declaration Order + +Variables and classes can be used before they are declared. + +[NOTE] +==== +In fact, declaration order is only significant for: + +* Functions with more than one input variable called with positional arguments, see <>. +* Functions with more than one output variable, see <>. +* Records that are used as arguments to external functions, see <>. +* Enumeration literal order within enumeration types, see <>. +==== + +=== Component Declarations + +Component declarations are described in this section. + +A _component declaration_ is an element of a class definition that generates a component. +A component declaration specifies (1) a component name, i.e., an identifier, (2) the class to be flattened in order to generate the component, and (3) an optional `Boolean` parameter expression. +Generation of the component is suppressed if this parameter expression evaluates to false. +A component declaration may be overridden by an element-redeclaration. + +A _component_ or _variable_ is an instance (object) generated by a component declaration. +Special kinds of components are scalar, array, and attribute. + +==== Syntax + +The formal syntax of a component declaration clause is given by the following syntactic rules: + +[source,grammar] +---- +component-clause : + type-prefix type-specifier [ array-subscripts ] component-list + +type-prefix : + [ flow | stream ] + [ discrete | parameter | constant ] + [ input | output ] + +type-specifier : + ["."] name + +component-list : + component-declaration { "," component-declaration } + +component-declaration : + declaration [ condition-attribute ] description + +condition-attribute : + if expression + +declaration : + IDENT [ array-subscripts ] [ modification ] +---- + +[NOTE] +==== +The declaration of a component states the type, access, variability, data flow, and other properties of the component. +A `component-clause`, i.e., the whole declaration, contains type prefixes followed by a `type-specifier` with optional `array-subscripts` followed by a `component-list`. + +There is no semantic difference between variables declared in a single declaration or in multiple declarations. +For example, regard the following single declaration (`component-clause`) of two matrix variables: + +[source,modelica] +---- +Real[2,2] A, B; +---- + +That declaration has the same meaning as the following two declarations together: + +[source,modelica] +---- +Real[2,2] A; +Real[2,2] B; +---- + +The array dimension descriptors may instead be placed after the variable name, giving the two declarations below, with the same meaning as in the previous example: + +[source,modelica] +---- +Real A[2,2]; +Real B[2,2]; +---- + +The following declaration is different, meaning that the variable a is a scalar but B is a matrix as above: + +[source,modelica] +---- +Real a, B[2,2]; +---- +==== + +==== Static Semantics + +If the `type-specifier` of the component declaration denotes a built-in type (`RealType`, `IntegerType`, etc.), the flattened or instantiated component has the same type. + +A class defined with `partial` in the `class-prefixes` is called a partial class. +Such a class is allowed to be incomplete, and cannot be instantiated in a simulation model; useful, e.g., as a base class. +See <> regarding short class definition semantics of propagating `partial`. + +If the `type-specifier` of the component does not denote a built-in type, the name of the type is looked up (see <>). +The found type is flattened with a new environment and the partially flattened enclosing class of the component. +It is an error if the type is partial in a simulation model, or if a simulation model itself is partial. +The new environment is the result of merging + +* the modification of enclosing class element-modification with the same name as the component +* the modification of the component declaration + +in that order. + +Array dimensions shall be scalar non-negative evaluable expressions of type `Integer`, a reference to a type (which must an enumeration type or `Boolean`, see <>), or the colon operator denoting that the array dimension is left unspecified (see <>). +All variants can also be part of short class definitions. + +[example] +==== +Example: Variables with array dimensions: + +[source,modelica] +---- +model ArrayVariants + type T = Real[:]; // Unspecified size for type + parameter T x = ones(4); + parameter T y[3] = ones(3, 4); + parameter Real a[2] = ones(2); // Specified using Integer + parameter Real b[2, 0] = ones(2, 0); // Size 0 is allowed + parameter Real c[:] = ones(0); // Unspecified size for variable + parameter Integer n = 0; + Real z[n*2] = cat(1, ones(n), zeros(n));// Parameter expressions are allowed + Boolean notV[Boolean] = {true, false}; // Indexing with type +end ArrayVariants; +---- +==== + +The rules for components in functions are described in <>. + +Conditional declarations of components are described in <>. + +===== Declaration Equations + +An environment that defines the value of a component of built-in type is said to define a declaration equation associated with the declared component. +These are a subset of the binding equations, see <>. +The declaration equation is of the form `x = expression` defined by a component declaration, where `expression` must not have higher variability than the declared component `x` (see <>). +Unlike other equations, a declaration equation can be overridden (replaced or removed) by an element modification. + +For declarations of vectors and matrices, declaration equations are associated with each element. + +Only components of the specialized classes `type`, `record`, `operator record`, and `connector`, or components of classes inheriting from `ExternalObject` may have declaration equations. +See also the corresponding rule for algorithms, <>. + +===== Prefix Rules + +A prefix is property of an element of a class definition which can be present or not be present, e.g., `final`, `public`, `flow`. + +Type prefixes (that is, `flow`, `stream`, `discrete`, `parameter`, `constant`, `input`, `output`) shall only be applied for type, record, operator record, and connector components -- see also record specialized class, <>. +This is further restricted below; some of these combinations of type prefixes and specialized classes are not legal. + +An exception is `input` for components whose type is of the specialized class `function` (these can only be used for function formal parameters and has special semantics, see <>). +In this case, the `input` prefix is not applied to the elements of the component, and the prefix is allowed even if the elements of the component have `input` or `output` prefix. + +In addition, instances of classes extending from `ExternalObject` may have type prefixes `parameter` and `constant`, and in functions also type prefixes `input` and `output`, see <>. + +Variables declared with the `stream` type prefix shall be a subtype of `Real`, or a `record` component where all the primitive elements shall be a subtype of `Real`. +The members of the record may not have the `stream` type prefix. +This is further restricted in <>. + +Variables declared with the `input` type prefix must not also have the prefix `parameter` or `constant`. + +The type prefix `flow` of a component that is not a primitive element (see <>), is also applied to the elements of the component (this is done after verifying that the type prefixes occurring on elements of the component are correct). +Primitive elements with the `flow` type prefix shall be a subtype of `Real`, `Integer`, or an operator record defining an additive group, see <>. + +The type prefixes `input` and `output` of a structured component (except as described above) are also applied to the elements of the component (this is done after verifying that the type prefixes occurring on elements of the component are correct). + +When any of the type prefixes `flow`, `input` and `output` are applied for a structured component, no element of the component may have any of these type prefixes, nor can they have `stream` prefix. +The corresponding rules for the type prefixes `discrete`, `parameter` and `constant` are described in <> for structured components. + +[NOTE] +The prefixes `flow`, `stream`, `input` and `output` could be treated more uniformly above, and instead rely on other rules forbidding combinations. +The type prefix `stream` can be applied to structured components, specifically records. +The type prefix `flow` can be applied to structured components, see <>. +Note that there are no specific restrictions if an operator record component has the type prefix `flow`, since the members of an operator record cannot have any of the prefixes `flow`, `stream`, `input` or `output`. + +[example] +Example: `input` can only be used, if none of the elements has a `flow`, `stream`, `input` or `output` type prefix. + +The prefixes `input` and `output` have a slightly different semantic meaning depending on the context where they are used: + +* In _functions_, these prefixes define the computational causality of the function body, i.e., given the variables declared as `input`, the variables declared as `output` are computed in the function body, see <>. + +* In _simulation models_ and _blocks_ (i.e., on the top level of a model or block that shall be simulated), these prefixes define the interaction with the environment where the simulation model or block is used. + Especially, the `input` prefix defines that values for such a variable have to be provided from the simulation environment and the `output` prefix defines that the values of the corresponding variable can be directly utilized in the simulation environment, see the notion of globally balanced in <>. + +* In component _models_ and _blocks_, the `input` prefix defines that a binding equation has to be provided for the corresponding variable when the component is utilized in order to guarantee a locally balanced model (i.e., the number of local equations is identical to the local number of unknowns), see <>. ++ +[example] +==== +Example: + +[source,modelica] +---- +block FirstOrder + input Real u; + ... +end FirstOrder; +model UseFirstOrder + FirstOrder firstOrder(u=time); // binding equation for u + ... +end UseFirstOrder; +---- +==== ++ +The `output` prefix does not have a particular effect in a model or block component and is ignored. + +* In _connectors_, prefixes `input` and `output` define that the corresponding connectors can only be connected according to block diagram semantics, see <> (e.g., a connector with an `output` variable can only be connected to a connector where the corresponding variable is declared as `input`). + There is the restriction that connectors which have at least one variable declared as `input` must be externally connected, see <> (in order to get a locally balanced model, where the number of local unknowns is identical to the number of unknown equations). + Together with the block diagram semantics rule this means, that such connectors must be connected _exactly once externally_. + +* In _records_, prefixes `input` and `output` are not allowed, since otherwise a record could not be, e.g., passed as input argument to a function. + +==== Component Variability Prefixes + +The prefixes `discrete`, `parameter`, `constant` of a component declaration are called variability prefixes and are the basis for defining in which situation the variable values of a component are initialized (see <> and <>) and when they are changed during simulation. +Further details on how the prefixes relate to component variability, as well as rules applying to components the different variabilities, are given in <>. + +==== Acyclic Bindings of Constants and Parameters + +For a constant or parameter `v` with declaration equation, the expression of the declaration equation in the flattended model must not depend on `v` itself, neither directly nor indirectly via other variables' declaration equations. +To satisfy this condition, dependencies shall be removed as needed by applying simplifications based on values of constants (except with `Evaluate = false`) and all other evaluable parameters (see <>) that don't depend on `v`. +It is not permitted to expand a record and/or non-scalar declaration equation into scalar equations to satisfy the condition. + +[example] +==== +Example: Direct and indirect cyclic dependency: + +[source,modelica] +---- +/* All of the following are illegal: */ +parameter Real r = 2 * sin(r); // Depends directly on r. +parameter Real p = 2 * q; // Indirect dependency on p via q = sin(p). +parameter Real q = sin(p); // Indirect dependency on q via p = 2 * q. +---- +==== + +[example] +==== +Example: While declaration equations must not be cyclical, the use of initial equations can still introduce valid cyclic dependencies between parameters: + +[source,modelica] +---- + parameter Real p = 2 * q; // This is the only declaration equation. + parameter Real q(fixed = false); +initial equation + q = sin(p); // OK, not a declaration equation. +---- +==== + +[example] +==== +Example: Breaking cyclic dependency. + +[source,modelica] +---- +model ABCD + parameter Real A[n, n]; + parameter Integer n = size(A, 1); +end ABCD; + +final ABCD a; +// Illegal cyclic dependency between size(a.A, 1) and a.n. + +ABCD b(redeclare Real A[2, 2] = [1, 2; 3, 4]); +// Legal since size of A is no longer dependent on n. + +ABCD c(n = 2); // Legal since n is no longer dependent on the size of A. + +partial model PartialLumpedVolume + parameter Boolean use_T_start = true "= true, use T_start, otherwise h_start" + annotation(Dialog(tab = "Initialization"), Evaluate = true); + parameter Medium.Temperature T_start=if use_T_start then system.T_start else + Medium.temperature_phX(p_start,h_start,X_start) + annotation(Dialog(tab = "Initialization", enable = use_T_start)); + parameter Medium.SpecificEnthalpy h_start=if use_T_start then + Medium.specificEnthalpy_pTX(p_start, T_start, X_start) else Medium.h_default + annotation(Dialog(tab = "Initialization", enable = not use_T_start)); +end PartialLumpedVolume; +// Cycle for T_start and h_start, but still valid since cycle disappears +// when evaluating use_T_start + +// The unexpanded bindings have illegal cycles for both x and y +// (even if they would disappear if bindings were expanded). +model HasCycles + parameter Integer n = 10; + final constant Real A[3, 3] = [0, 0, 0; 1, 0, 0; 2, 3, 0]; + parameter Real y[3] = A * y + ones(3); + parameter Real x[n] = cat(1, {3.4}, x[1:(n-1)]); +end HasCycles; +---- +==== + +==== Conditional Component Declaration + +A component declaration can have a condition-attribute: `if` _expression_. + +[example] +==== +Example: + +[source,modelica] +---- + parameter Boolean electric = true; + parameter Boolean heatPort = false; + Motor motor; + Level1 component1(J=J) if electric "Conditional component"; + Level2 component2(J=component1.J) if not electric "Conditional component"; + // Illegal modifier on component2 since component1.J does not exist when component2 exists. + Level3 component3(J=component1.J) if electric and heatPort "Conditional component"; + // Legal modifier since component1 always exists if component3 exists +equation + connect(component1..., ...); // Connection to conditional component 1 + connect(component2.n, motor.n); // Connection to conditional component 2 + connect(component3.n, motor.n); // Connection to conditional component 3 + component1.u=0; // Not a good idea, is illegal if electric is false +---- +==== + +The _expression_ must be a `Boolean` scalar expression, and must be an evaluable expression. + +[NOTE] +An evaluable expression is required since it shall be evaluated at compile time. + +A redeclaration of a component shall not include a condition attribute; and the condition attribute is kept from the original declaration (see <>). + +If the `Boolean` expression is `false`, the component (including its modifier) is removed from the flattened DAE, and connections to/from the component are removed. +Such a component can only be modified, used in connections, and/or used in a modifier of another conditional component with a `false` condition. + +There are no restrictions on the component if the `Boolean` expression is `true`. + +[NOTE] +==== +Adding the component and then removing it ensures that the component is valid. + +If a `connect`-equation defines the connection of a non-conditional component `c1` with a conditional component `c2` and `c2` is de-activated, then `c1` must still be a declared element. + +There are annotations to handle the case where the connector should be connected when activated, see <>. +==== + +=== Component Variability + +As briefly mentioned in <>, the component variability prefixes are the basis for defining component variability. +Combined with some other information about the components and analysis of expression variability (<>), they define the component variabilities as follows: + +* A variable `vc` declared with `constant` prefix does not change during simulation, with a value that is unaffected even by the initialization problem (i.e., determined during translation). + This is called a _constant_, or _constant variable_. + For further details, see <>. + +* A variable `ep` is called an _evaluable parameter variable_ if all of the following applies: ++ +** It is declared with the `parameter` prefix. +** It has `fixed = true`. +** It does not have annotation `Evaluate = false`. +** The declaration equation -- or `start`-attribute if no declaration equation is given (see <>) -- is given by an evaluable expression (<>). + ++ +It is also simply called an _evaluable parameter_. +An evaluable parameter does not change during transient analysis, with a value either determined during translation (similar to having prefix `constant`, and is then called an _evaluated parameter_) or by the initialization problem (similar to a _non-evaluable parameter_, see item below). +At which of these stages the value is determined is tool dependent. +For further details, see <>. + +* A variable `np` declared with the `parameter` prefix, is called a _non-evaluable parameter variable_ unless it is an evaluable parameter. +It is also simply called a _non-evaluable parameter_. +It does not change during transient analysis, with a value determined by the initialization problem. +For further details, see <>. + +* A _discrete-time variable_ `vd` is a variable that is discrete-valued (that is, not of `Real` type) or assigned in a `when`-clause. + The `discrete` prefix may be used to clarify that a variable is discrete-time. + During transient analysis the variable can only change its value at event instants (see <>). + For further details, see <>. + +* A _continuous-time variable_ is a `Real` variable without any prefix that is not assigned in a `when`-clause. +The variable can change both continuously and discontinuously at any time. +For further details, see <>. + +The term _parameter variable_ or just _parameter_ refers to a variable that is either an evaluable or non-evaluable parameter variable. + +The variability of expressions and restrictions on variability for declaration equations is given in <>. + +[NOTE] +==== +Note that discrete-time expressions include parameter expressions, whereas discrete-time variables do not include parameter variables. +The reason can intuitively be explained as follows: + +* When discussing variables we also want to consider them as left-hand-side variables in assignments, and thus a lower variability would be a problem. + +* When discussing expressions we only consider them as right-hand-side expressions in those assignment, and thus a lower variability can automatically be included; and additionally we have sub-expressions where lower variability is not an issue. + +For `Real` variables we can distinguish two subtly different categories: discrete-time and piecewise constant, where the discrete-time variables are a subset of all piecewise constant variables. +The `Real` variables declared with the prefix `discrete` is a subset of the discrete-time `Real` variables. +For a `Real` variable, being discrete-time is equivalent to being assigned in a `when`-clause. +A variable used as argument to `pre` outside a `when`-clause must be discrete-time. + +[source,modelica] +---- +model PiecewiseConstantReals + discrete Real xd1 "Must be assigned in a when-clause, discrete-time"; + Real xd2 "Assigned in a when-clause (below) and thus discrete-time"; + Real xc3 "Not discrete-time, but piecewise constant"; + Real x4 "Piecewise constant, but changes between events"; +equation + when sample(1, 1) then + xd1 = pre(xd1) + 1; + xd2 = pre(xd2) + 1; + end when; + // It is legal to use pre for a discrete-time variable outside of when + xc3 = xd1 + pre(xd2); + // But pre(xc3) would not be legal + x4 = if noEvent(cos(time) > 0.5) then 1.0 else -1.0; +end PiecewiseConstantReals; +---- + +Tools may optimize code to only compute and store discrete-time variables at events. +Tools may extend that optimization to piece-wise constant variables that only change at events (in the example above `xc3`). +As shown above variables can be piecewise constant, but change at times that are not events (in the example above `x4`). +It is not clear how a tool could detect and optimize the latter case. + +A `parameter` variable is constant during simulation. +This prefix gives the library designer the possibility to express that the physical equations in a library are only valid if some of the used components are constant during simulation. +The same also holds for discrete-time and constant variables. +Additionally, the `parameter` prefix allows a convenient graphical user interface in an experiment environment, to support quick changes of the most important constants of a compiled model. +In combination with an `if`-equation, a `parameter` prefix allows removing parts of a model before the symbolic processing of a model takes place in order to avoid variable causalities in the model (similar to `#ifdef` in C). +Class parameters can be sometimes used as an alternative. + +Example: + +[source,modelica] +---- +model Inertia + parameter Boolean state = true; + ... +equation + J * a = t1 - t2; + if state then // code which is removed during symbolic + der(v) = a; // processing, if state=false + der(r) = v; + end if; +end Inertia; +---- + +A constant variable is similar to a parameter with the difference that constants cannot be changed after translation and usually not changed after they have been given a value. +It can be used to represent mathematical constants, e.g.: + +[source,modelica] +---- +final constant Real PI = 4 * atan(1); +---- + +There are no continuous-time `Boolean`, `Integer` or `String` variables. +In the rare cases they are needed they can be faked by using `Real` variables, e.g.: + +[source,modelica] +---- + Boolean off1, off1a; + Real off2; +equation + off1 = s1 < 0; + off1a = noEvent(s1 < 0); // error, since off1a is discrete + off2 = if noEvent(s2 < 0) then 1 else 0; // possible + u1 = if off1 then s1 else 0; // state events + u2 = if noEvent(off2 > 0.5) then s2 else 0; // no state events +---- + +Since `off1` is a discrete-time variable, state events are generated such that `off1` is only changed at event instants. +Variable `off2` may change its value during continuous integration. +Therefore, `u1` is guaranteed to be continuous during continuous integration whereas no such guarantee exists for `u2`. +==== + +==== Constants + +Constant variables (defined in <>) shall have an associated declaration equation with a constant expression, if the constant is directly in the simulation model, or used in the simulation model. +The value of a constant can be modified after it has been given a value, unless the constant is declared `final` or modified with a `final` modifier. +A constant without an associated declaration equation can be given one by using a modifier. + +By the acyclic binding rule in <>, it follows that the value of a constant (or evaluable parameter, see below) to be used in simplifications is possible to obtain by evaluation of an evaluable expression where values are available for all component subexpressions. + +==== Parameters + +Parameter variables are divided into evaluable parameter variables and non-evaluable parameter variables, both defined in <>. + +By the acyclic binding rule in <>, it follows that a value for an evaluable parameter is possible to obtain during translation, compare <>. +Making use of that value during translation turns the evaluable parameter into an evaluated parameter, and it must be ensured that the parameter cannot be assigned a different value after translation, as this would invalidate the use of the original value during translation. + + +[example] +==== +Example: A particularly demanding aspect of this evaluation is the potential presence of external functions. +Hence, if it is known that a parameter won't be used by an evaluable expression, a user can make it clear that the external function is not meant to be evaluated during translation by using `Evaluate = false`: + +[source,modelica] +---- +import length = Modelica.Utilities.Strings.length; // Pure external function +parameter Integer n = length("Hello"); // Evaluable parameter +parameter Integer p = length("Hello") + annotation(Evaluate = false); // Non-evaluable parameter +parameter Boolean b = false; // Evaluable parameter + +/* Fulfillment of acyclic binding rule might cause evaluation of n; + * to break the cycle, a tool might evaluate either b, n, or both: + */ +parameter Real x = if b and n < 3 then 1 - x else 0; + +/* Fulfillment of acyclic binding rule cannot cause evaluation of p; + * to break the cycle, evaluation of b is the only option: + */ +parameter Real y = if b and p < 3 then 1 - y else 0; +---- +==== + +[NOTE] +For a parameter in a valid model, presence of `Evaluate` makes it possible to tell immediately whether it is an evaluable or non-evaluable parameter, at least as long as the warning described in <> isn't triggered. +To see this, note that `Evaluate = false` makes it a non-evaluable parameter by definition, and that `Evaluate = true` would trigger the warning if the parameter is non-evaluable. + +[NOTE] +==== +With every non-evaluable parameter, there is at least one reason why it isn't an evaluable parameter. +This information is useful to maintain in tools, as it allows generation of informative error messages when a violation of evaluable expression variability is detected. +For example: + +[source,modelica] +---- + parameter Integer n = + if b then 1 else 2; // Non-evaluable parameter due to variability of b. + parameter Boolean b(fixed = false); + // Non-evaluable parameter due to fixed = false. + Real[n] x; // Variability error: n must be evaluable. +initial equation + b = n > 3; +---- + +Here, a good error message for the variability error can include the information that the reason for `n` being a non-evaluable parameter is that it has a dependency on the non-evaluable parameter `b`. +==== + +[NOTE] +Related to evaluable parameters, the term structural parameter is also used in the Modelica community. +This term has no meaning defined by the specification, and the meaning may vary from one context to another. +One common meaning, however, is that in the context of a given tool, a parameter is called structural if the tool has decided to evaluate it because it controls some variation of the equation structure that the tool is unable to leave undecided during translation. +With this interpretation of structural parameter, it follows that such a structural parameter must also be an evaluable parameter, while there are typically many evaluable parameters that are not structural. + +==== Discrete-Time Variables + +A discrete-time variable (defined in <>) has a vanishing time derivative between events. +Note that this is not the same as saying that `der(vd) = 0` almost everywhere, as the derivative is not even defined at the events. +It is not allowed to apply `der` to discrete-time variables. + +If a `Real` variable in a simulation model is declared with the prefix `discrete`, it must be assigned in a `when`-clause, either by an assignment or an equation. +The variable assigned in a `when`-clause shall not be defined in a sub-component of `model` or `block` specialized class. +(This is to keep the property of balanced models.) + +A `Real` variable assigned in a `when`-clause is a discrete-time variable, even though it was not declared with the prefix `discrete`. +A `Real` variable not assigned in any `when`-clause and without any type prefix is a continuous-time variable. + +The default variability for `Integer`, `String`, `Boolean`, or `enumeration` variables is discrete-time, and it is not possible to declare continuous-time `Integer`, `String`, `Boolean`, or `enumeration` variables. + +[NOTE] +The restriction that discrete-valued variables (of type `Boolean`, etc) cannot be declared with continuous-time variability is one of the foundations of the expression variability rules that will ensure that any discrete-valued expression has at most discrete-time variability, see <>. + +==== Continuous-Time Variables + +A continuous-time variable (defined in <>) `vn` may have a non-vanishing time derivative (provided `der(vn)` is allowed this can be expressed as `der(vn) <> 0`) and may also change its value discontinuously at any time during transient analysis (see <>). +It may also contain a combination of these effects. +Regarding existence of `der(vn)`, see <>. + +==== Variability of Structured Entities + +For elements of structured entities with variability prefixes the most restrictive of the variability prefix and the variability of the component wins (using the default variability for the component if there is no variability prefix on the component). + +[example] +==== +Example: + +[source,modelica] +---- +record A + constant Real pi = 3.14; + Real y; + Integer i; +end A; + +parameter A a; + // a.pi is a constant + // a.y and a.i are parameters + +A b; + // b.pi is a constant + // b.y is a continuous-time variable + // b.i is a discrete-time variable +---- +==== + +=== Class Declarations + +Essentially everything in Modelica is a class, from the predefined classes `Integer` and `Real`, to large packages such as the Modelica standard library. +The description consists of a class definition, a modification environment that modifies the class definition, an optional list of dimension expressions if the class is an array class, and a lexically enclosing class for all classes. + +The object generated by a class is called an instance. +An instance contains zero or more components (i.e., instances), equations, algorithms, and local classes. +An instance has a type (see <>). + +[example] +==== +Example: + +A rather typical structure of a Modelica class is shown below. +A class with a name, containing a number of declarations followed by a number of equations in an equation section. + +[source,modelica] +---- +class ClassName + Declaration1 + Declaration2 + ... +equation + equation1 + equation2 + ... +end ClassName; +---- +==== + +The following is the formal syntax of class definitions, including the special variants described in later sections. + +An element is part of a class definition, and is one of: class definition, component declaration, or `extends`-clause. +Component declarations and class definitions are called named elements. +An element is either inherited from a base class or local. + +[source,grammar] +---- +class-definition : + [ encapsulated ] class-prefixes class-specifier + +class-prefixes : + [ partial ] + ( class | model | [ operator ] record | block | [ expandable ] connector | type | + package | [ ( pure | impure ) ] [ operator ] function | operator ) + +class-specifier : + long-class-specifier | short-class-specifier | der-class-specifier + +long-class-specifier : + IDENT description-string composition end IDENT + | extends IDENT [ class-modification ] description-string composition + end IDENT + +short-class-specifier : + IDENT "=" base-prefix type-specifier [ array-subscripts ] + [ class-modification ] description + | IDENT "=" enumeration "(" ( [enum-list] | ":" ) ")" description + +der-class-specifier : + IDENT "=" der "(" type-specifier "," IDENT { "," IDENT } ")" description + +base-prefix : + [ input | output ] + +enum-list : enumeration-literal { "," enumeration-literal} + +enumeration-literal : IDENT description + +composition : + element-list + { public element-list | + protected element-list | + equation-section | + algorithm-section + } + [ external [ language-specification ] + [ external-function-call ] [ annotation-clause ] ";" + ] + [ annotation-clause ";" ] +---- + +==== Short Class Definitions + +A _short class definition_ is a class definition in the form + +[source,modelica] +---- +class IDENT1 = type-specifier class-modification; +---- + +Except that `type-specifier` (the base-class) may be replaceable, and that the short class definition does not introduce an additional lexical scope for modifiers, it is identical to the longer form + +[source,modelica] +---- +class IDENT1 + extends type-specifier class-modification; +end IDENT1; +---- + +An exception to the above is that if the short class definition is declared as `encapsulated`, then the type-specifier and modifiers follow the rules for encapsulated classes and cannot be looked up in the enclosing scope. + +[example] +==== +Example: Demonstrating the difference in scopes: +[source,modelica] +---- +model Resistor + parameter Real R; + ... +end Resistor; +model A + parameter Real R; + replaceable model Load=Resistor(R=R) constrainedby TwoPin; + // Correct, sets the R in Resistor to R from model A. + replaceable model LoadError + extends Resistor(R=R); + // Gives the singular equation R=R, since the right-hand side R + // is searched for in LoadError and found in its base class Resistor. + end LoadError constrainedby TwoPin; + encapsulated model Load2=.Resistor(R=2); // Ok + encapsulated model LoadR=.Resistor(R=R); // Illegal + Load a,b,c; + ConstantSource ...; + ... +end A; +---- + +The type-specifiers `.Resistor` rely on global name lookup (see <>), due to the encapsulated restriction. +==== + +A short class definition of the form + +[source,modelica] +---- +type TN = T[N] (optional modifier); +---- + +where N represents arbitrary array dimensions, conceptually yields an array class + +[source,modelica] +---- +'array' TN + T[n] _ (optional modifiers); +'end' TN; +---- + +Such an array class has exactly one anonymous component (_); see also <>. +When a component of such an array class type is flattened, the resulting flattened component type is an array type with the same dimensions as _ and with the optional modifier applied. + +[example] +==== +Example: The types of `f1` and `f2` are identical: + +[source,modelica] +---- +type Force = Real[3](unit={"Nm","Nm","Nm"}); +Force f1; +Real f2[3](unit={"Nm","Nm","Nm"}); +---- +==== + +If a short class definition inherits from a partial class the new class definition will be partial, regardless of whether it is declared with the prefix `partial` or not. + +[example] +==== +Example: + +[source,modelica] +---- +replaceable model Load=TwoPin; +Load R; // Error unless Load is redeclared since TwoPin is a partial class. +---- +==== + +If a short class definition does not specify any specialized class the new class definition will inherit the specialized class (this rule applies iteratively and also for redeclare). + +A `base-prefix` applied in the `short-class-definition` does not influence its type, but is applied to components declared of this type or types derived from it; see also <>. + +[example] +==== +Example: + +[source,modelica] +---- +type InArgument = input Real; +type OutArgument = output Real[3]; + +function foo + InArgument u; // Same as: input Real u + OutArgument y; // Same as: output Real[3] y +algorithm + y:=fill(u,3); +end foo; + +Real x[:]=foo(time); +---- +==== + +==== Combining Base Classes and Other Elements + +It is not legal to combine equations, algorithms, components, non-empty base classes (see below), or protected elements with an extends from an array class, a class with non-empty `base-prefix`, a _simple type_ (`Real`, `Boolean`, `Integer`, `String` and enumeration types), or any class transitively extending from an array class, a class with non-empty `base-prefix`, or a simple type. + +[[defintion:empty-class,Defintion Empty class]] +Defintion Empty class:: +A class without equations, algorithms, or components, and where any base-classes are themselves empty. + +[NOTE] +An empty class may contain annotations, such as graphics, and can be used more freely as base-class than other classes. + +[example] +==== +Example: + +[source,modelica] +---- +model Integrator + input Real u; + output Real y = x; + Real x; +equation + der(x) = u; +end Integrator; + +model Integrators = Integrator[3]; // Legal + +model IllegalModel + extends Integrators; + Real x; // Illegal combination of component and array class +end IllegalModel; + +connector IllegalConnector + extends Real; + Real y; // Illegal combination of component and simple type +end IllegalConnector; +---- +==== + +==== Local Class Definitions -- Nested Classes + +The local class should be statically flattenable with the partially flattened enclosing class of the local class apart from local class components that are `partial` or `outer`. +The environment is the modification of any enclosing class element modification with the same name as the local class, or an empty environment. + +The unflattened local class together with its environment becomes an element of the flattened enclosing class. + +[example] +==== +Example: The following example demonstrates parameterization of a local class: + +[source,modelica] +---- +model C1 + type Voltage = Real(nominal=1); + Voltage v1, v2; +end C1; + +model C2 + extends C1(Voltage(nominal=1000)); +end C2; +---- + +Flattening of class `C2` yields a local class `Voltage` with `nominal` modifier `1000`. +The variables `v1` and `v2` are instances of this local class and thus have a nominal value of 1000. +==== + +=== Specialized Classes + +Specialized kinds of classes (earlier known as restricted classes) +`record`, `type`, `model`, `block`, `package`, `function` and `connector` +have the properties of a general class, apart from restrictions. +Moreover, they have additional properties called enhancements. +The definitions of the specialized classes are given below (additional restrictions on inheritance are in <>): + +* `record` -- Only public sections are allowed in the definition or in any of its components (i.e., `equation`, `algorithm`, `initial equation`, `initial algorithm` and `protected` sections are not allowed). + The elements of a record shall not have prefixes `input`, `output`, `inner`, `outer`, `stream,` or `flow`. + Enhanced with implicitly available record constructor function, see <>. + The components directly declared in a record may only be of specialized class `record` or `type`. +* `type` -- May only be predefined types, enumerations, array of `type`, or classes extending from `type`. +* `model` -- The normal modeling class in Modelica. +* `block` -- Same as `model` with the restriction that each public connector component of a `block` must have prefixes `input` and/or `output` for all connector variables that are neither `parameter` nor `constant`. ++ +[NOTE] +The purpose is to model input/output blocks of block diagrams. +Due to the restrictions on `input` and `output` prefixes, connections between blocks are only possible according to block diagram semantic. +* `function` -- See <> for restrictions and enhancements of functions. + Enhanced to allow the function to contain an external function interface. ++ +[NOTE] +Non-`function` specialized classes do not have this property. +* `connector` -- Only public sections are allowed in the definition or in any of its components (i.e., `equation`, `algorithm`, `initial equation`, `initial algorithm` and `protected` sections are not allowed). + Enhanced to allow `connect` to components of connector classes. + The elements of a connector shall not have prefixes `inner`, or `outer`. + May only contain components of specialized class `connector`, `record` and `type`. +* `package` -- May only contain declarations of classes and constants. + Enhanced to allow `import` of elements of packages. + (See also <> on packages.) +* `operator record` -- Similar to `record`; but operator overloading is possible, and due to this the typing rules are different, see <>. + It is not legal to extend from an `operator record` (or `connector` inheriting from `operator record`), except if the new class is an `operator record` or `connector` that is declared as a short class definition, whose modifier is either empty or only modify the default attributes for the component elements directly inside the `operator record`. + An `operator record` can only extend from an `operator record`. + It is not legal to extend from any of its enclosing scopes. + (See <>). +* `operator` -- May only contain declarations of functions. + May only be placed directly in an `operator record`. + (See also <>). +* `operator function` -- Shorthand for an `operator` with exactly one function; same restriction as `function` class and in addition may only be placed directly in an `operator record`. ++ +[NOTE] +==== +A function declaration +[source,modelica] +---- +operator function foo ... end foo; +---- +is conceptually treated as +[source,modelica] +---- +operator foo function foo1 + ... +end foo1; end foo; +---- +==== + +Additionally only components which are of specialized classes `record`, `type`, `operator record`, and connector classes based on any of those can be used as component references in normal expressions and in the left hand side of assignments, subject to normal type compatibility rules. +Additionally components of connectors may be arguments of `connect`-equations, and any component can be used as argument to the `ndims` and `size`-functions, or for accessing elements of that component (possibly in combination with array indexing). + +[example] +==== +Example: Use of `operator`: + +[source,modelica] +---- +operator record Complex + Real re; + Real im; + ... + encapsulated operator function '*' + import Complex; + input Complex c1; + input Complex c2; + output Complex result; + algorithm + result := Complex(re=c1.re*c2.re - c1.im*c2.im, + im=c1.re*c2.im + c1.im*c2.re); + end '*'; +end Complex; +record MyComplex + extends Complex; // Error; extending from enclosing scope. + Real k; +end MyComplex; +operator record ComplexVoltage = Complex(re(unit="V"),im(unit="V")); // allowed +---- +==== + +=== Balanced Models + +[NOTE] +==== +In this section restrictions for `model` and `block` classes are present, in order that missing or too many equations can be detected and localized by a Modelica translator before using the respective `model` or `block` class. +A non-trivial case is demonstrated in the following example: + +[source,modelica] +---- +partial model BaseCorrelation + input Real x; + Real y; +end BaseCorrelation; + +model SpecialCorrelation // correct in Modelica 2.2 and 3.0 + extends BaseCorrelation(x=2); +equation + y=2/x; +end SpecialCorrelation; + +model UseCorrelation // correct according to Modelica 2.2 + // not valid according to Modelica 3.0 + replaceable model Correlation=BaseCorrelation; + Correlation correlation; +equation + correlation.y=time; +end UseCorrelation; + +model Broken // after redeclaration, there is 1 equation too much in Modelica 2.2 + UseCorrelation example(redeclare Correlation=SpecialCorrelation); +end Broken; +---- + +In this case one can argue that both `UseCorrelation` (adding an acausal equation) and `SpecialCorrelation` (adding a default to an input) are correct. +Still, when combined they lead to a model with too many equations, and it is not possible to determine which model is incorrect without strict rules -- as the ones defined here. + +In Modelica 2.2, model `Broken` will work with some models. +However, by just redeclaring it to model `SpecialCorrelation`, an error will occur and it will be very difficult in a larger model to figure out the source of this error. + +In Modelica 3.0, model `UseCorrelation` is no longer allowed and the translator will give an error. +In fact, it is guaranteed that a redeclaration cannot lead to an unbalanced model any more. +==== + +The restrictions below apply after flattening -- i.e., inherited components are included -- possibly modified. +The corresponding restrictions on connectors and connections are in <>. + +Definition Local number of unknowns:: ++ +-- +The local number of unknowns of a `model` or `block` class is the sum based on the components: + +* For each declared component of specialized class `type` (`Real`, `Integer`, `String`, `Boolean`, enumeration and arrays of those, etc.) or `record`, or `operator record` not declared as `outer`, it is the number of unknown variables inside it (i.e., excluding parameters and constants and counting the elements after expanding all records, operator record, and arrays to a set of scalars of primitive types). + +* Each declared component of specialized class `type` or `record` declared as `outer` is ignored. ++ +[NOTE] +I.e., all variables inside the component are treated as known. + +* For each declared component of specialized class `connector` component, it is the number of unknown variables inside it (i.e., excluding parameters and constants and counting the elements after expanding all records and arrays to a set of scalars of primitive types). + +* For each declared component of specialized class `block` or `model`, it is the sum of the number of inputs and flow variables in the (top level) public connector components of these components (and counting the elements after expanding all records and arrays to a set of scalars of primitive types). +-- + +Definition Local equation size:: ++ +-- +The local equation size of a `model` or `block` class is the sum of the following numbers: + +* The number of equations defined locally (i.e., not in any `model` or `block` component), including binding equations, and equations generated from `connect`-equations. ++ +[NOTE] +This includes the proper count for `when`-clauses (see <>), and algorithms (see <>), and is also used for the flat Hybrid DAE formulation (see <>). + +* The number of input and flow variables present in each (top-level) public connector component. ++ +[NOTE] +This represents the number of connection equations that will be provided when the class is used, due to the balancing restrictions for connectors, see <>. + +* The number of (top-level) public input variables that neither are connectors nor have binding equations. ++ +[NOTE] +I.e., top-level inputs are treated as known variables. +This represents the number of binding equations that will be provided when the class is used. + +* For over-determined connectors, <>, each spanning tree without any root node adds the difference between the size of the over-determined type or record and the size of the output of `equalityConstraint`. ++ +[NOTE] +By definition this term is zero in simulation models, but relevant for checking component models. +There are no other changes in the variable and equation count for models -- but a restriction on the size of the output of `equalityConstraint`, <>. +-- + +[NOTE] +-- +To clarify top-level inputs without binding equation (for non-inherited inputs binding equation is identical to declaration equation, but binding equations also include the case where another model extends `M` and has a modifier on `u` giving the value): + +[source,modelica] +---- +model M + input Real u; + input Real u2=2; +end M; +---- + +Here `u` and `u2` are top-level inputs and not connectors. +The variable `u2` has a binding equation, but `u` does not have a binding equation. +In the equation count, it is assumed that an equation for `u` is supplied when using the model. +-- + +Definition: Locally balanced:: +A `model` or `block` class is locally balanced if the local number of unknowns is identical to the local equation size for all legal values of constants and parameters. ++ +[NOTE] +Here, legal values must respect final bindings and min/max-restrictions. +A tool shall verify the locally balanced property for the actual values of parameters and constants in the simulation model. +It is a quality of implementation for a tool to verify this property in general, due to arrays of (locally) undefined sizes, conditional declarations, `for`-loops etc. + +Globally balanced:: +Similarly as locally balanced, but including all unknowns and equations from all components. +The global number of unknowns is computed by expanding all unknowns (i.e., excluding parameters and constants) into a set of scalars of primitive types. +This should match the global equation size defined as: +* The number of equations defined (included in any `model` or `block` component), including equations generated from `connect`-equations. +* The number of input and flow variables present in each (top-level) public connector component. +* The number of (top-level) public input variables that neither are connectors nor have binding equations. ++ +[NOTE] +I.e., top-level inputs are treated as known variables. + +The following restrictions hold: + +* In a non-partial `model` or `block`, all non-connector inputs of `model` or `block` components must have binding equations. ++ +[NOTE] +E.g., if the model contains a component, `firstOrder` (of specialized class `model`) and `firstOrder` has `input Real u` then there must be a binding equation for `firstOrder.u`. +Note that this also applies to components inherited from a partial base-class provided the current class is non-partial. + +* A component declared with the `inner` or `outer` prefix shall not be of a class having top-level public connectors containing inputs. + +* In a declaration of a component of a record, connector, or simple type, modifiers can be applied to any element, and these are also considered for the equation count. ++ +[example] +==== +Example: + +[source,modelica] +---- +Flange support(phi=phi, tau=torque1+torque2) if use_support; +---- +==== ++ +If `use_support=true`, there are two additional equations for `support.phi` and `support.tau` via the modifier. + +* In a declarations of a component of a `model` or `block` class, modifiers shall only contain redeclarations of replaceable elements and binding equations. +The binding equations in modifiers for components may in these cases only be for parameters, constants, inputs and variables having a default binding equation. +For the latter case of variables having a default binding equation the modifier may not remove the binding equation using `break`, see <>. + +* Modifiers of base-classes (on extends and short class definitions) shall only contain redeclarations of replaceable elements and binding equations. +The binding equations follow the corresponding rules above, as if they were applied to the inherited component. + +* All non-partial `model` and `block` classes must be locally balanced. ++ +[NOTE] +This means that the local number of unknowns equals the local equation size. + +Based on these restrictions, the following strong guarantee can be given: + +* All simulation models and blocks are globally balanced. + +[NOTE] +Therefore the number of unknowns equal to the number of equations of a simulation model or block, provided that every used non-partial `model` or `block` class is locally balanced. + +[example] +==== +Example 1: + +[source,modelica] +---- +connector Pin + Real v; + flow Real i; +end Pin; + +model Capacitor + parameter Real C; + Pin p, n; + Real u; +equation + 0 = p.i + n.i; + u = p.v - n.v; + C*der(u) = p.i; +end Capacitor; +---- + +Model `Capacitor` is a locally balanced model according to the following analysis: + +Locally unknown variables: `p.i`, `p.v`, `n.i`, `n.v`, `u` + +Local equations: +[latexmath] +++++ +\begin{align*} +0 &= p.i + n.i;\\ +u &= p.v - n.v;\\ +C \cdot \text{der}(u) &= p.i; +\end{align*} +++++ +and 2 equations corresponding to the 2 flow variables `p.i` and `n.i`. + +These are 5 equations in 5 unknowns (locally balanced model). +A more detailed analysis would reveal that this is structurally non-singular, i.e., that the hybrid DAE will not contain a singularity independent of actual values. + +If the equation `u = p.v - n.v` would be missing in the `Capacitor` model, there would be 4 equations in 5 unknowns and the model would be locally unbalanced and thus simulation models in which this model is used would be usually structurally singular and thus not solvable. + +If the equation `u = p.v - n.v` would be replaced by the equation `u = 0` and the equation `C*der(u) = p.i` would be replaced by the equation `C*der(u) = 0`, there would be 5 equations in 5 unknowns (locally balanced), but the equations would be singular, regardless of how the equations corresponding to the flow variables are constructed because the information that `u` is constant is given twice in a slightly different form. +==== + +[example] +==== +Example 2: + +[source,modelica] +---- +connector Pin + Real v; + flow Real i; +end Pin; + +partial model TwoPin + Pin p,n; +end TwoPin; + +model Capacitor + parameter Real C; + extends TwoPin; + Real u; +equation + 0 = p.i + n.i; + u = p.v - n.v; + C*der(u) = p.i; +end Capacitor; + +model Circuit + extends TwoPin; + replaceable TwoPin t; + Capacitor c(C=12); +equation + connect(p, t.p); + connect(t.n, c.p); + connect(c.n, n); +end Circuit; +---- + +Since `t` is partial we cannot check whether this is a globally balanced model, but we can check that `Circuit` is locally balanced. + +Counting on model `Circuit` results in the following balance sheet: + +Locally unknown variables (8): `p.i`, `p.v`, `n.i`, `n.v`, and 2 flow variables for `t` (`t.p.i`, `t.n.i`), and 2 flow variables for `c` (`c.p.i`, `c.n.i`). + +Local equations: +[latexmath] +++++ +\begin{align} +\text{p.v} &= \text{t.p.v} \\ +0 &= \text{p.i}-\text{t.p.i} \\ +\text{c.p.v} &= \text{t.n.v} \\ +0 &= \text{c.p.i}+\text{t.n.i} \\ +\text{n.v} &= \text{c.n.v} \\ +0 &= \text{n.i}-\text{c.n.i} +\end{align} +++++ +and 2 equation corresponding to the flow variables `p.i`, `n.i`. + +In total we have 8 scalar unknowns and 8 scalar equations, i.e., a locally balanced model (and this feature holds for any models used for the replaceable component `t`). + +Some more analysis reveals that this local set of equations and unknowns is structurally non-singular. +However, this does not provide any guarantees for the global set of equations, and specific combinations of models that are locally non-singular may lead to a globally singular model. +==== + +[example] +==== +Example 3: + +[source,modelica] +---- +import Modelica.Units.SI; + +partial model BaseProperties "Interface of medium model" + parameter Boolean preferredStates = false; + constant Integer nXi "Number of independent mass fractions"; + InputAbsolutePressure p; + InputSpecificEnthalpy h; + InputMassFraction Xi[nXi]; + SI.Temperature T; + SI.Density d; + SI.SpecificInternalEnergy u; + + connector InputAbsolutePressure = input SI.AbsolutePressure; + connector InputSpecificEnthalpy = input SI.SpecificEnthalpy; + connector InputMassFraction = input SI.MassFraction; +end BaseProperties; +---- + +The model `BaseProperties` together with its use in derived classes and as component relies on a special design pattern defined below. +The variables `p`, `h`, `Xi` are marked as input to get correct equation count. +Since they are connectors they should neither be given binding equations in derived classes nor when using the model. +The design pattern, which is used in this case, is to give textual equations for them (as below); using `connect`-equations for these connectors would be possible (and would work) but is not part of the design pattern. + +This partial model defines that `T`, `d`, `u` can be computed from the medium model, provided `p`, `h`, `Xi` are given. +Every medium with one or multiple substances and one or multiple phases, including incompressible media, has the property that `T`, `d`, `u` can be computed from `p`, `h`, `Xi`. +A particular medium may have different ``independent variables'' from which all other intrinsic thermodynamic variables can be recursively computed. +For example, a simple air model could be defined as: + +[source,modelica] +---- +model SimpleAir "Medium model of simple air. Independent variables: p, T" + extends BaseProperties( + nXi = 0, + p(stateSelect = + if preferredStates then StateSelect.prefer else StateSelect.default), + T(stateSelect = + if preferredStates then StateSelect.prefer else StateSelect.default)); + constant SI.SpecificHeatCapacity R = 287; + constant SI.SpecificHeatCapacity cp = 1005.45; + constant SI.Temperature T0 = 298.15 +equation + d = p/(R*T); + h = cp*(T-T0); + u = h - p/d; +end SimpleAir; +---- + +The local number of unknowns in model `SimpleAir` (after flattening) is: + +* 3 (`T`, `d`, `u`: variables defined in `BaseProperties` and inherited in `SimpleAir`), plus + +* 2 + `nXi` (`p`, `h`, `Xi`: variables inside connectors defined in `BaseProperties` and inherited in `SimpleAir`) + +resulting in 5 + `nXi` unknowns. +The local equation size is: + +* 3 (equations defined in `SimpleAir`), plus + +* 2 + `nXi` (input variables in the connectors inherited from `BaseProperties`) + +Therefore, the model is locally balanced. + +The generic medium model `BaseProperties` is used as a `replaceable model` in different components, like a dynamic volume or a fixed boundary condition: + +[source,modelica] +---- +import Modelica.Units.SI; + +connector FluidPort + replaceable model Medium = BaseProperties; + SI.AbsolutePressure p; + flow SI.MassFlowRate m_flow; + SI.SpecificEnthalpy h; + flow SI.EnthalpyFlowRate H_flow; + SI.MassFraction Xi [Medium.nXi] "Independent mixture mass fractions"; + flow SI.MassFlowRate mXi_flow[Medium.nXi] + "Independent subst. mass flow rates"; +end FluidPort; + +model DynamicVolume + parameter SI.Volume V; + replaceable model Medium = BaseProperties; + FluidPort port(redeclare model Medium = Medium); + Medium medium(preferredStates = true); // No modifier for p, h, Xi + SI.InternalEnergy U; + SI.Mass M; + SI.Mass MXi[medium.nXi]; +equation + U = medium.u*M; + M = medium.d*V; + MXi = medium.Xi*M; + der(U) = port.H_flow; // Energy balance + der(M) = port.m_flow; // Mass balance + der(MXi) = port.mXi_flow; // Substance mass balance +// Equations binding to medium (inputs) + medium.p = port.p; + medium.h = port.h; + medium.Xi = port.Xi; +end DynamicVolume; +---- + +The local number of unknowns of `DynamicVolume` is: + +* 4 + 2 * `nXi` (inside the `port` connector), plus + +* 2 + `nXi` (variables `U`, `M` and `MXi`), plus + +* 2 + `nXi` (the input variables in the connectors of the `medium` model) + +resulting in 8 + 4 * `nXi` unknowns; the local equation size is + +* 6 + 3 * `nXi` from the equation section, plus + +* 2 + `nXi` flow variables in the `port` connector. + +Therefore, `DynamicVolume` is a locally balanced model. + +Note, when the `DynamicVolume` is used and the `Medium` model is redeclared to `SimpleAir`, then a tool will try to select `p`, `T` as states, since these variables have `StateSelect.prefer` in the `SimpleAir` model (this means that the default states `U`, `M` are derived quantities). +If this state selection is performed, all intrinsic medium variables are computed from `medium.p` and `medium.T`, although `p` and `h` are the input arguments to the medium model. +This demonstrates that in Modelica input/output does not define the computational causality. +Instead, it defines that equations have to be provided here for `p`, `h`, `Xi`, in order that the equation count is correct. +The actual computational causality can be different as it is demonstrated with the `SimpleAir` model. + +[source,modelica] +---- +model FixedBoundary_pTX + parameter SI.AbsolutePressure p "Predefined boundary pressure"; + parameter SI.Temperature T "Predefined boundary temperature"; + parameter SI.MassFraction Xi[medium.nXi] + "Predefined boundary mass fraction"; + replaceable model Medium = BaseProperties; + FluidPort port(redeclare model Medium = Medium); + Medium medium; +equation + port.p = p; + port.H_flow = semiLinear(port.m_flow, port.h , medium.h); + port.MXi_flow = semiLinear(port.m_flow, port.Xi, medium.Xi); +// Equations binding to medium (note: T is not an input). + medium.p = p; + medium.T = T; + medium.Xi = Xi; +end FixedBoundary_pTX; +---- + +The number of local variables in `FixedBoundary_pTX` is: + +* 4 + 2 * `nXi` (inside the `port` connector), plus + +* 2 + `nXi` (the input variables in the connectors of the `medium` model) + +resulting in 6 + 3 * `nXi` unknowns, while the local equation size is + +* 4 + 2 * `nXi` from the equation section, plus + +* 2 + `nXi` flow variables in the `port` connector. + +Therefore, `FixedBoundary_pTX` is a locally balanced model. +The predefined boundary variables `p` and `Xi` are provided via equations to the input arguments `medium.p` and `medium.Xi`, in addition there is an equation for `T` in the same way -- even though `T` is not an input. +Depending on the flow direction, either the specific enthalpy in the port (`port.h`) or `h` is used to compute the enthalpy flow rate `H_flow`. +`h` is provided as binding equation to the medium. +With the equation `medium.T = T`, the specific enthalpy `h` of the reservoir is indirectly computed via the medium equations. +Again, this demonstrates, that an `input` just defines the number of equations have to be provided, but that it not necessarily defines the computational causality. +==== + +=== Predefined Types and Classes + +The attributes of the predefined variable types (`Real`, `Integer`, `Boolean`, `String`) and `enumeration` types are described below with Modelica syntax although they are predefined. +All attributes are predefined and attribute values can only be defined using a modification, such as in `Real x(unit = "kg")`. +Attributes cannot be accessed using dot notation, and are not constrained by equations and algorithm sections. + +The __ in the definitions of the predefined types represents the value of an expresion of that type. +Unlike attributes, the __ of a component cannot be referred to by name; both access and modification of the value is made directly on the component. + +[example] +==== +Example: Accessing and modifying a variable value, using `Real` as example of a predefined type: + +[source,modelica] +---- +model M + record R + Real u; + Real v; + end R; + Real x = sin(time); // Value modification. + Real y(unit = "kg") = x; // Access value of x, and modify value of y. + R r(u = y); // Value modification of r.u. +equation + r.v + x * x = 0; // Access values of r.v and x. +end M; +---- +Note that only the values of `x` and `y` are declared to be equal, but not their `unit`-attributes, nor any other attribute of `x` and `y` +==== + +It is not possible to combine extends from the predefined types, enumeration types, or this `Clock` type with other components. + +The names `Real`, `Integer`, `Boolean` and `String` have restrictions similar to keywords, see <>. + +[NOTE] +Hence, it is possible to define a normal class called `Clock` in a package and extend from it. + +[NOTE] +It also follows that the only way to declare a subtype of, e.g., `Real` is to use the `extends` mechanism. + +The definitions use `RealType`, `IntegerType`, `BooleanType`, `StringType`, `EnumType` as mnemonics corresponding to machine representations. +These are called the primitive types. + +Defintion Fallback value:: +In situations where the `start`-attribute would apply if provided, but the attribute is not provided, the fallback value shall be used instead. +Tools are recommended to give diagnostics when the fallback value is used. +The fallback values for variables of the different predefined types are defined below. + +==== Real Type + +The following is the predefined `Real` type: +[source,modelica] +---- +type Real // Note: Defined with Modelica syntax although predefined + RealType ; // Not an attribute; only accessed without dot-notation + parameter StringType quantity = ""; + parameter StringType unit = "" "Unit used in equations"; + parameter StringType displayUnit = "" "Default display unit"; + parameter RealType min = ...; // Lower bound for + parameter RealType max = ...; // Upper bound for + parameter RealType start; // Initial value + parameter BooleanType fixed = ...; // Enforce exact value of 'start' + parameter RealType nominal; // Nominal value + parameter BooleanType unbounded = false; // For error control + parameter StateSelect stateSelect = StateSelect.default; +equation + assert(min <= and <= max, "Variable value out of limit"); +end Real; +---- + +The default `min`- and `max`-attributes are the minimum and maximum representable finite floating point numbers of `RealType`. +The must be a finite floating point number, and thus the default `min`- and `max`-attributes do not impose any further constraints. + +[NOTE] +Of the representable floating point numbers, the minimum number is usually the negation of the maximum number, and should not be confused with the minimum positive number. + +The default `fixed`-attribute is `true` for parameters and constants, and `false` for other variables. + +The following attributes shall be evaluable: `quantity`, `unit`, `displayUnit`, `fixed`, and `stateSelect`. + +The `quantity`-attribute is used to impose a constraint on connection sets, see <>. + +The `unit`- and `displayUnit`-attributes may be either the empty string or a string matching `unit-expression` in <>. +The meaning of the empty string depends on the context. +For the input and output components of a function, the empty string allows different units to be used in different calls to the function. +For a non-function component, the empty string allows the unit (or display unit) to be inferred by the tool. + +[NOTE] +That `displayUnit` is evaluable allows tools to verify that the default display unit is consistent with the `unit`. +Unlike the `unit`, `displayUnit` is just a default, tools may allow using other compatible display units for a translated model. + +The `nominal`-attribute is meant to be used for scaling purposes and to define tolerances in relative terms, see <>. + +The fallback value is the closest value to 0.0 consistent with the `min` and `max` bounds. + +[NOTE] +For external functions in C89, `RealType` maps to `double`. +In the mapping proposed in Annex~F of the C99 standard, `RealType`/`double` matches the IEC~60559:1989 (ANSI/IEEE~754-1985) `double` format. + +==== Integer Type + +The following is the predefined `Integer` type: +[source,modelica] +---- +type Integer // Note: Defined with Modelica syntax although predefined + IntegerType ; // Not an attribute; only accessed without dot-notation + parameter StringType quantity = ""; + parameter IntegerType min = ...; // Lower bound for + parameter IntegerType max = ...; // Upper bound for + parameter IntegerType start; // Initial value + parameter BooleanType fixed = ...; // Enforce exact value of 'start' +equation + assert(min <= and <= max, "Variable value out of limit"); +end Integer; +---- + +The following attributes shall be evaluable: `quantity`, and `fixed`. + +The `quantity`-attribute is used to impose a constraint on connection sets, see <>. + +The default `min`- and `max`-attributes are the minimum and maximum numbers of `IntegerType`. +The minimal recommended number range for `IntegerType` is from -2147483648 to +2147483647, corresponding to a two's-complement 32-bit integer implementation. + +[NOTE] +Note that -2147483648 as a Modelica expression is the negation of a number above the minimal recommended number range. +To express the lower limit with all intermediate results within the minimal recommended number range, one may write -2147483647 - 1 instead. + +The fallback value is the closest value to 0 consistent with the `min` and `max` bounds. + +The default `fixed`-attribute is `true` for parameters and constants, and `false` for other variables. + +==== Boolean Type +:id: boolean-type + +The following is the predefined `Boolean` type (defined with Modelica syntax although predefined): + +[source,modelica] +---- +type Boolean + BooleanType ; // Not an attribute; only accessed without dot-notation + parameter StringType quantity = ""; + parameter BooleanType start; // Initial value + parameter BooleanType fixed = ...; // Enforce exact value of 'start' +end Boolean; +---- + +The following attributes shall be evaluable: `quantity`, and `fixed`. + +The `quantity` attribute is used to impose a constraint on connection sets, see <>. + +The fallback value is `false`. + +The default `fixed` attribute is `true` for parameters and constants, and `false` for other variables. + +==== String Type +:id: string-type + +The following is the predefined `String` type (defined with Modelica syntax although predefined): + +[source,modelica] +---- +type String + StringType ; // Not an attribute; only accessed without dot-notation + parameter StringType quantity = ""; + parameter StringType start; // Initial value + parameter BooleanType fixed = ...; // Enforce exact value of 'start' +end String; +---- + +The following attributes shall be evaluable: `quantity`, and `fixed`. + +The `quantity` attribute is used to impose a constraint on connection sets, see <>. + +A `StringType` value (such as `` or other textual attributes of built-in types) may contain any Unicode data (and nothing else). + +The fallback value is `""`. + +The default `fixed` attribute is `true` for parameters and constants, and `false` for other variables. + +==== Enumeration Types +:id: enumeration-types + +A declaration of the form + +[source,modelica] +---- +type E = enumeration([enum-list]); +---- + +defines an enumeration type `E` and the associated enumeration literals of the enum-list. +The enumeration literals shall be distinct within the enumeration type. +The names of the enumeration literals are defined inside the scope of `E`. +Each enumeration literal in the `enum-list` has type `E`. + +[example] +==== +Example: + +[source,modelica] +---- +type Size = enumeration(small, medium, large, xlarge); +Size t_shirt_size = Size.medium; +---- +==== + +An optional description string can be specified with each enumeration literal. + +[example] +==== +Example: + +[source,modelica] +---- +type Size2 = enumeration(small "1st", medium "2nd", large "3rd", xlarge "4th"); +---- +==== + +An enumeration type is a simple type and the attributes are defined in <>. +The `Boolean` type name or an enumeration type name can be used to specify the dimension range for a dimension in an array declaration and to specify the range in a `for`-loop range expression; see <>. +An element of an enumeration type can be accessed in an expression. + +[NOTE] +Uses of elements of enumeration type in expressions include indexing into an array. + +[example] +==== +Example: + +[source,modelica] +---- +type DigitalCurrentChoices = enumeration(zero, one); +// Similar to Real, Integer +---- + +Setting attributes: + +[source,modelica] +---- +type DigitalCurrent = DigitalCurrentChoices(quantity="Current", + start = DigitalCurrentChoices.one, fixed = true); +DigitalCurrent c(start = DigitalCurrent.one, fixed = true); +DigitalCurrentChoices c(start = DigitalCurrentChoices.one, fixed = true); +---- + +Using enumeration types as expressions: + +[source,modelica] +---- +Real x[DigitalCurrentChoices]; + +// Example using the type name to represent the range +for e in DigitalCurrentChoices loop + x[e] := 0.; +end for; + +for e loop // Equivalent example using short form + x[e] := 0.; +end for; + +// Equivalent example using the colon range constructor +for e in DigitalCurrentChoices.zero : DigitalCurrentChoices.one loop + x[e] := 0.; +end for; + +model Mixing1 "Mixing of multi-substance flows, alternative 1" + replaceable type E=enumeration(:)"Substances in Fluid"; + input Real c1[E], c2[E], mdot1, mdot2; + output Real c3[E], mdot3; +equation + 0 = mdot1 + mdot2 + mdot3; + for e in E loop + 0 = mdot1*c1[e] + mdot2*c2[e]+ mdot3*c3[e]; + end for; + /* Array operations on enumerations are NOT (yet) possible: + zeros(n) = mdot1*c1 + mdot2*c2 + mdot3*c3 // error + */ +end Mixing1; + +model Mixing2 "Mixing of multi-substance flows, alternative 2" + replaceable type E=enumeration(:)"Substances in Fluid"; + input Real c1[E], c2[E], mdot1, mdot2; + output Real c3[E], mdot3; +protected + // No efficiency loss, since cc1, cc2, cc3 + // may be removed during translation + Real cc1[:]=c1, cc2[:]=c2, cc3[:]=c3; + final parameter Integer n = size(cc1,1); +equation + 0 = mdot1 + mdot2 + mdot3; + zeros(n) = mdot1*cc1 + mdot2*cc2 + mdot3*cc3 +end Mixing2; +---- +==== + +===== Attributes of Enumeration Types +:id: attributes-of-enumeration-types + +For each enumeration: + +[source,modelica] +---- +type E = enumeration(e1, e2, ..., en); +---- + +a new simple type is conceptually defined as + +[source,modelica] +---- +type E // Note : Defined with Modelica syntax although predefined + EnumType ; // Not an attribute; only accessed without dot-notation + parameter StringType quantity = ""; + parameter EnumType min = e1, max = en; + parameter EnumType start; // Initial value + parameter BooleanType fixed = true, // default for parameter/constant; + = false; // default for other variables + constant EnumType e1 = ...; + ... + constant EnumType en = ...; +equation + assert(min <= and <= max, "Variable value out of limit"); +end E; +---- + +The following attributes shall be evaluable: `quantity`, and `fixed`. + +The `quantity` attribute is used to impose a constraint on connection sets, see <>. + +The fallback value is the `min` bound. + +[NOTE] +Since the attributes and enumeration literals are on the same level, it is not possible to use the enumeration attribute names (`quantity`, `min`, `max`, `start`, `fixed`) as enumeration literals. + +===== Conversion of Enumeration to String or Integer +:id: conversion-of-enumeration-to-string-or-integer +:id: type-conversion-of-enumeration-values-to-string-or-integer + +The type conversion function `Integer()` returns the ordinal number of the enumeration value `E.enumvalue`, to which the expression is evaluated, where `Integer(E.e1) = 1`, `Integer(E.en) = n`, for an enumeration type `E = enumeration(e1, ..., en)`. + +`String(E.enumvalue)` gives the `String` representation of the enumeration value. + +[example] +==== +Example: `String(E.Small)` gives `"Small"`. +==== + +See also <>. + +===== Conversion of Integer to Enumeration +:id: conversion-of-integer-to-enumeration +:id: type-conversion-of-integer-to-enumeration-values + +Whenever an enumeration type is defined, a type conversion function with the same name and in the same scope as the enumeration type is implicitly defined. +This function can be used in an expression to convert an integer value to the corresponding (as described in <>) enumeration value. + +For an enumeration type named `EnumTypeName`, the expression `EnumTypeName()` returns the enumeration value `EnumTypeName.e` such that `Integer(EnumTypeName.e)` is equal to the original integer expression. + +Attempting to convert an integer argument that does not correspond to a value of the enumeration type is an error. + +[example] +==== +Example: + +[source,modelica] +---- +type Colors = enumeration ( RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW ); +---- + +Converting from `Integer` to `Colors`: + +[source,modelica] +---- +c = Colors(i); +c = Colors(10); // An error +---- +==== + +===== Unspecified Enumeration +:id: unspecified-enumeration + +An enumeration type defined using `enumeration(:)` is unspecified and can be used as a replaceable enumeration type that can be freely redeclared to any enumeration type. +There can be no enumeration variables declared using `enumeration(:)` in a simulation model. + +==== Attributes start, fixed, nominal, and unbounded +:id: attributes-start-fixed-nominal-and-unbounded + +The attributes `start` and `fixed` define the initial conditions for a variable. +`fixed = false` means an initial guess, i.e., value may be changed by static analyzer. +`fixed = true` means a required value. +The resulting consistent set of values for all model variables is used as initial values for the analysis to be performed. + +The attribute `nominal` gives the nominal value for the variable. +The user need not set it even though the standard does not define a default value. +The lack of default allows the tool to propagate the nominal attribute based on equations, and if there is no value to propagate the tool should use a non-zero value, it may use additional information (e.g., `min` attribute) to find a suitable value, and as last resort use 1. +If `unbounded = true` it indicates that the state may grow without bound, and the error in absolute terms shall be controlled. + +[NOTE] +The nominal value can be used by an analysis tool to determine appropriate tolerances or epsilons, or may be used for scaling. +For example, the tolerance for an integrator could be computed as `tol * (abs(nominal) + (if x.unbounded then 0 else abs(x)))`. +A default value is not provided in order that in cases such as `a = b`, where `b` has a nominal value but not `a`, the nominal value can be propagated to the other variable. + +==== Other Predefined Types +:id: other-predefined-types + +===== StateSelect +:id: stateselect + +The predefined `StateSelect` enumeration type is the type of the `stateSelect` attribute of the `Real` type. +It is used to explicitly control state selection. + +[source,modelica] +---- +type StateSelect = enumeration( + never "Do not use as state at all.", + avoid "Use as state, if it cannot be avoided (but only if variable appears " + + "differentiated and no other potential state with attribute " + + "default, prefer, or always can be selected).", + default "Use as state if appropriate, but only if variable appears " + + "differentiated.", + prefer "Prefer it as state over those having the default value " + + "(also variables can be selected, which do not appear " + + "differentiated).", + always "Do use it as a state." +); +---- + +===== AssertionLevel +:id: assertionlevel + +The predefined `AssertionLevel` enumeration type is used together with `assert`, see <>. + +[source,modelica] +---- +type AssertionLevel = enumeration(warning, error); +---- + +===== Connections + +The package `Connections` is used for over-constrained connection graphs, see <>. + +===== ExternalObject + +See <> for information about the predefined type `ExternalObject`. + +===== Clock Types + +See <> and <>. + +===== Graphical Annotation Types + +A number of "predefined" record types and enumeration types for graphical annotations are described in <>. +These types are not predefined in the usual sense since they cannot be referenced in ordinary Modelica code, only within annotations, see <>. \ No newline at end of file diff --git a/docs/5___scoping.adoc b/docs/5___scoping.adoc new file mode 100644 index 000000000..63b42e42a --- /dev/null +++ b/docs/5___scoping.adoc @@ -0,0 +1,580 @@ +== Scoping, Name Lookup, and Flattening +:id: scoping-name-lookup-and-flattening + +This chapter describes the scope rules, and most of the name lookup and flattening of Modelica. + +=== Flattening Context + +Flattening is made in a context which consists of a modification environment (see <>) and an ordered set of enclosing classes. + +=== Enclosing Classes + +The classes lexically enclosing an element form an ordered set of enclosing classes. +A class defined inside another class definition (the enclosing class) precedes its enclosing class definition in this set. + +Enclosing all class definitions is an unnamed enclosing class that contains all top-level class definitions, and not-yet read classes defined externally as described in <>. +The order of top-level class definitions in the unnamed enclosing class is undefined. + +During flattening, the enclosing class of an element being flattened is a partially flattened class. + +[NOTE] +For example, this means that a declaration can refer to a name inherited through an `extends`-clause. + +.Example +[source,modelica] +---- +class C1 ... end C1; +class C2 ... end C2; +class C3 + Real x = 3; + C1 y; + class C4 + Real z; + end C4; +end C3; +---- + +The unnamed enclosing class of class definition `C3` contains `C1`, `C2`, and `C3` in arbitrary order. +When flattening class definition `C3`, the set of enclosing classes of the declaration of `x` is the partially flattened class `C3` followed by the unnamed enclosing class with `C1`, `C2`, and `C3`. +The set of enclosing classes of `z` is `C4`, `C3` and the unnamed enclosing class in that order. + +=== Static Name Lookup + +Names are looked up at class flattening to find names of base classes, component types, etc. +Implicitly defined names of record constructor functions and enumeration type conversion functions are ignored during type name lookup. +Names of record classes and enumeration types are ignored during function name lookup. + +[NOTE] +The reason to ignore the implicitly defined names is that a record and the implicitly created record constructor function, see <>, and an enumeration type and the implicitly created conversion function (<>), have the same name. + +==== Simple Name Lookup + +A class declared with the keyword `encapsulated` (see <> in the grammar) is called an `encapsulated` class. +By restricting name lookup inside a restricted class in ways defined in this chapter, the meaning of the class is made independent of where it is placed in a package hierarchy. + +When an element, equation, or section is flattened, any simple name (not composed using dot notation) is first looked up sequentially among iteration variables (if any; see below), and then looked up sequentially in each member of the ordered set of `instance scopes` (see <>) corresponding to lexically `enclosing classes` until a match is found or an enclosing class is encapsulated. +In the latter case the lookup stops except for the predefined types, functions and operators defined in this specification. +For these cases the lookup continues in the global scope, where they are defined. + +The iteration variables are the implicitly declared iteration variable(s) if inside the body of a `for`-loop (see <> and <>), or the body of a reduction expression (see <>). + +Reference to variables successfully looked up in an enclosing class is only allowed for variables declared as `constant`. +The values of modifiers are thus resolved in the instance scope of which the modifier appears; if the use is in a modifier on a short class definition, see <>. + +This lookup in each `instance` scope is performed as follows: + +* Among declared named elements (`class`-definition and `component-declaration`) of the class (including elements inherited from base classes). +* Among the import names of qualified `import`-clauses in the `instance scope`. + The `import name` of `import A.B.C;` is `C` and the import name of `import D = A.B.C;` is `D`. +* Among the public members of packages imported via unqualified import-clauses in the `instance` scope. + It is an error if this step produces matches from several unqualified imports. + +The `import`-clauses defined in inherited classes are ignored for the lookup, i.e., `import`-clauses are not inherited. + +==== Composite Name Lookup + +For a composite name of the form `A.B` or `A.B.C`, etc. lookup is performed as follows: + +* The first identifier (`A`) is looked up as defined above. +* If the first identifier denotes a component, the rest of the name (e.g., `B` or `B.C`) is looked up among the declared named component elements of the component. +* If not found, and if the first identifier denotes a component and the composite name is used as a function call, the lookup is also performed among the declared elements of the component, and must find a non-operator function. + Each leading element, including the first one, must in this case be a scalar component, or `component[j]` where `component` is an array of components and the indices `j` are evaluable expressions and `component[j]` is a scalar. + All identifiers of the rest of the name (e.g., `B` and `B.C`) must be classes. + That is, the composite name is comprised of one or more component names (optionally with indexing), followed by one or more class names. +* If the identifier denotes a class, that class is temporarily flattened (as if instantiating a component without modifiers of this class, see <> and using the enclosing classes of the denoted class). + The rest of the name (e.g., `B` or `B.C`) is looked up among the declared named elements of the temporary flattened class. + The lookup will only find the element (assuming it exists) in the following cases: + ** If the class is declared as `package` or `operator` (but not `operator record` or `operator function`) all elements can be found. + ** An element can be found if it is declared as `encapsulated`. + ** A deprecated case is that if the class satisfies the requirements for a `package` (without being declared as such), it is still treated as a `package`. + The class we look inside shall not be partial in a simulation model. + +[NOTE] +The temporary class flattening performed for composite names follow the same rules as class flattening of the base class in an `extends`-clause, local classes and the type in a component-clause, except that the environment is empty. +See also `MoistAir2` example in <> for further explanations regarding looking inside partial packages. + +[example] +==== +Example: Components and classes are part of the same name-space and thus a component cannot have the same name as its class or the first part of the class-name as that would prevent lookup of the class name. + +[source,modelica] +---- +model A + M M; // Illegal, component 'M' prevents finding class 'M' + P.Q P; // Illegal, component 'P' prevents finding package 'P' + .R R; // Legal, see next section + S.Q Q; // Legal + + Y a; // Illegal, component 'Y' (below) prevents finding class 'Y' + Y.X b; // Illegal, component 'Y' (below) prevents finding package 'Y' + .Y c; // Legal, see next section + Real Y; +end A; +---- +==== + +[NOTE] +Note that an `operator` class may only contain declarations of functions and thus fulfills the requirements for a package (see <>). +In practice, the non-deprecated rules imply that we can call `Complex.'-'.negate` and `Complex.'\+'` for the example in <>. +This requires that `operator '-'` and `operator function '+'` are declared as `encapsulated` as in the example. + +==== Global Name Lookup + +For a name starting with dot, e.g., `.A` (or `.A.B`, `.A.B.C` etc.) lookup is performed as follows: + +* The first identifier (`A`) is looked up in the global scope. + This is possible even if the class is encapsulated and `import`-clauses are not used for this. + If there does not exist a class `A` in global scope this is an error. + +* If the name is simple then the class `A` is the result of lookup. + +* If the name is a composite name then the class `A` is temporarily flattened with an empty environment (i.e., no modifiers, see <> and using the enclosing classes of the denoted class). + The rest of the name (e.g., `B` or `B.C`) is looked up among the declared named elements of the temporary flattened class. + If the class does not satisfy the requirements for a package, the lookup is restricted to encapsulated elements only. + The class we look inside shall not be partial. + +[NOTE] +The package-restriction ensures that global name lookup of component references can only find global constants. + +==== Lookup of Imported Names + +See <>. + +=== Inner Declarations - Instance Hierarchy Name Lookup + +An element declared with the prefix `outer` references an element instance with the same name but using the prefix `inner` which is nearest in the enclosing instance hierarchy of the `outer` element declaration. + +Outer component declarations shall not have modifications (including binding equations). +Outer class declarations should be defined using short-class definitions without modifications. +However, see also <>. + +If the outer component declaration is a disabled conditional component (see <>) it is also ignored for the automatic creation of inner component (neither causing it; nor influencing the type of it). + +An `outer` element reference in a simulation model requires that one corresponding `inner` element declaration exists or can be created in a unique way: + +* If there are two (or more) `outer` declarations with the same name, both lacking matching `inner` declarations, and the `outer` declarations are not of the same class it is an error. +* If there is one (or more) `outer` declarations of a partial class it is an error. +* In other cases, i.e., if a unique non-partial class is used for all `outer` declarations of the same name lacking a matching inner declaration, then an `inner` declaration of that class is automatically added at the top of the model and a diagnostic is given. +* The annotations defined in <> does not affect this process, other than that: + ** `missingInnerMessage` can be used for the diagnostic (and possibly error messages) + +An `outer` element component may be of a partial class (but the referenced `inner` component must be of a non-partial class). + +[NOTE] +`inner`/`outer` components may be used to model simple fields, where some physical quantities, such as gravity vector, environment temperature or environment pressure, are accessible from all components in a specific model hierarchy. +Inner components are accessible throughout the model, if they are not "shadowed" by a corresponding `inner` declaration in a more deeply nested level of the model hierarchy. + +[example] +==== +Simple Example: + +[source,modelica] +---- +class A + outer Real T0; + ... +end A; +class B + inner Real T0=1; + A a1, a2; // B.T0, B.a1.T0 and B.a2.T0 will have the same value + A a3(T0=4); // Illegal as T0 is an outer variable. + ... +end B; +---- + +More complicated example: + +[source,modelica] +---- +class A + outer Real TI; + class B + Real TI; + class C + Real TI; + class D + outer Real TI; + end D; + D d; + end C; + C c; + end B; + B b; +end A; + +class E + inner Real TI; + class F + inner Real TI; + class G + Real TI; + class H + A a; + end H; + H h; + end G; + G g; + end F; + F f; +end E; + +class I + inner Real TI; + E e; + // e.f.g.h.a.TI, e.f.g.h.a.b.c.d.TI, and e.f.TI is the same variable + // But e.f.TI, e.TI and TI are different variables + A a; // a.TI, a.b.c.d.TI, and TI is the same variable +end I; +---- +==== + +The `inner` component shall be a subtype of the corresponding `outer` component. + +[NOTE] +If the two types are not identical, the type of the `inner` component defines the instance and the `outer` component references just part of the `inner` component. + +[example] +==== +Example: + +[source,modelica] +---- +class A + inner Real TI; + class B + outer Integer TI; // error, since A.TI is no subtype of A.B.TI + end B; +end A; +---- +==== + +==== Field Functions Using Inner/Outer + +[NOTE] +==== +Inner declarations can be used to define field functions, such as position dependent gravity fields, e.g.: + +[source,modelica] +---- +partial function A + input Real u; + output Real y; +end A; + +function B // B is a subtype of A + extends A; +algorithm + ... +end B; + +class D + outer function fc = A; + ... +equation + y = fc(u); +end D; + +class C + inner function fc = B; // define function to be actually used + D d; // The equation is now treated as y = B(u) +end C; +---- +==== + +=== Simultaneous Inner/Outer Declarations + +An element declared with both the prefixes `inner` and `outer` conceptually introduces two declarations with the same name: one that follows the above rules for `inner` and another that follows the rules for `outer`. + +[NOTE] +Local references for elements with both the prefix `inner` and `outer` references the `outer` element. +That in turn references the corresponding element in an enclosing scope with the prefix `inner`. + +Modifications of elements declared with both the prefixes `inner` and `outer` may have modifications, those modifications are only applied to the `inner` declaration. + +[example] +==== +Example: + +[source,modelica] +---- +class A + outer parameter Real p=2; // error, since modification +end A; +---- + +Intent of the following example: Propagate _enabled_ through the hierarchy, and also be able to disable subsystems locally. + +[source,modelica] +---- +model ConditionalIntegrator "Simple differential equation if isEnabled" + outer Boolean isEnabled; + Real x(start = 1); +equation + der(x) = if isEnabled then -x else 0; +end ConditionalIntegrator; + +model SubSystem "Subsystem that enables its conditional integrators" + Boolean enableMe = time <= 1; + // Set inner isEnabled to outer isEnabled and enableMe + inner outer Boolean isEnabled = isEnabled and enableMe; + ConditionalIntegrator conditionalIntegrator; + ConditionalIntegrator conditionalIntegrator2; +end SubSystem; + +model System + SubSystem subSystem; + inner Boolean isEnabled = time >= 0.5; + // subSystem.conditionalIntegrator.isEnabled will be + // 'isEnabled and subSystem.enableMe' +end System; +---- +==== + +=== Flattening Process + +In order to guarantee that elements can be used before they are declared and that elements do not depend on the order of their declaration (see <>) in the enclosing class, the flattening proceeds in the following two major steps: + +. Instantiation process +. Generation of the flat equation system + +The result is an equation system of all equations/algorithms, initial equations/algorithms and instances of referenced functions. +Modifications of constants, parameters and variables are included in the form of equations. + +The constants, parameters and variables are defined by globally unique identifiers and all references are resolved to the identifier of the referenced variable. +No other transformations are performed. + +==== Instantiation + +The instantiation is performed in two steps. +First a class tree is created and then from that an instance tree for a particular model is built up. +This forms the basis for derivation of the flat equation system. + +An implementation may delay and/or omit building parts of these trees, which means that the different steps can be interleaved. +If an error occurs in a part of the tree that is not used for the model to be instantiated the corresponding diagnostics can be omitted (or be given). +However, errors that should only be reported in a simulation model must be omitted there, since they are not part of the simulation model. + +===== The Class Tree + +All necessary libraries including the model which is to be instantiated are loaded (e.g., from a file system) and form a so called class tree. +This tree represents the syntactic information from the class definitions. +It contains also all modifications at their original locations in syntactic form. +The built-in classes are put into the unnamed root of the class tree. + +[NOTE] +The class tree is built up directly during parsing of the Modelica texts. +For each class a local tree is created which is then merged into the one big tree, according to the location of the class in the class hierarchy. +This tree can be seen as the abstract syntax tree (AST) of the loaded libraries. + +===== The Instance Tree + +The output of the instantiation process is an instance tree. +The instance tree consists of nodes representing the elements of a class definition from the class tree. +For a component the subtree of a particular node is created using the information from the class of the component-clause and a new modification environment as result of merging the current modification environment with the modifications from the current element declaration (see <>). + +The instance tree has the following properties: + +* It contains the instantiated elements of the class definitions, with redeclarations taken into account and merged modifications applied. +* Each instance knows its source class definition from the class tree and its modification environment. +* Each modification knows its instance scope. + +The instance tree is used for lookup during instantiation. +To be prepared for that, it has to be based on the structure of the class tree with respect to the class definitions. +The built-in classes are instantiated and put in the unnamed root prior to the instantiation of the user classes, to be able to find them. + +[NOTE] +The existence of the two separate trees (instance tree and class tree) is conceptual. +Whether they really exist or are merged into only one tree or the needed information is held completely differently is an implementation detail. +It is also a matter of implementation to have only these classes instantiated which are needed to instantiate the class of interest. + +A node in the instance tree is the instance scope for the modifiers and elements syntactically defined in the class it is instantiated from. +The instance scope is the starting point for name lookup. + +[NOTE] +If the name is not found the lookup is continued in the instance scope corresponding to the lexically enclosing class. +`extends`-clauses are treated as unnamed nodes in the instance tree -- when searching for an element in an instance scope the search also recursively examines the elements of the `extends`-clauses. +Except that inherited `import`-clauses are ignored. + +===== The Instantiation Procedure + +The instantiation is a recursive procedure with the following inputs: + +* the class to be instantiated (current class) +* the modification environment with all applicable redeclarations and merged modifications (initially empty) +* a reference to the node of the instance tree, which the new instance should go into (parent instance) + +The instantiation starts with the class to be instantiated, an empty modification environment, and an unnamed root node as parent node. + +During instantiation all lookup is performed using the instance tree, starting from the instance scope of the current element. +References in modifications and equations are resolved later (during generation of flat equation system) using the same lookup. + +===== Steps of Instantiation + +The element itself:: ++ +A _partially instantiated_ class or component is an element that is ready to be instantiated; a partially instantiated element (i.e., class or component) is comprised of a reference to the original element (from the class tree) and the modifiers for that element (including a possible redeclaration). ++ +The possible redeclaration of the element itself takes effect. ++ +The class of a partially instantiated component is found in the instance tree (using the redeclaration if any), modifiers merged to that class forming a new partially instantiated class that is instantiated as below. + +The local contents of the element:: ++ +For local classes and components in the current class, instance nodes are created and inserted into the current instance. +Modifiers (including class redeclarations) are merged and associated with the instance and the element is partially instantiated. ++ +[NOTE] +The partially instantiated elements are used later for lookup during the generation of the flat equation system and are instantiated fully, if necessary, using the stored modification environment. ++ +Equations, algorithms, and annotations of the class and the component declaration are copied to the instance without merging. ++ +[NOTE] +The annotations can be relevant for simulations, e.g., annotations for symbolic processing (<>), simulation experiments (<>) or functions (<> and <>). ++ +The `extends`-clauses are not looked up, but empty `extends`-clause nodes are created and inserted into the current instance (to be able to preserve the declaration order of components). + +[[the-inherited-contents-of-the-element]] +The inherited contents of the element:: ++ +Classes of `extends`-clauses of the current class are looked up in the instance tree, modifiers (including redeclarations) are merged, the contents of these classes are partially instantiated using the new modification environment, and are inserted into an `extends`-clause node, which is an unnamed node in the current instance that only contains the inherited contents from that base class. ++ +The classes of `extends`-clauses are looked up before and after handling `extends`-clauses; and it is an error if those lookups generate different results. ++ +At the end, the current instance is checked whether their children (including children of `extends`-clauses) with the same name are identical and only the first one of them is kept. +It is an error if they are not identical. ++ +[NOTE] +Only keeping the first among the children with the same name is important for function arguments where the order matters. + +Recursive instantiation of components:: ++ +Components (local and inherited) are recursively instantiated. ++ +[example] +==== +As an example, consider: + +[source,modelica] +---- +model M + model B + A a; + replaceable model A = C; + type E = Boolean; + end B; + B b(redeclare model A = D (p=1)); + partial model C + E e; + end C; + + model D + extends C; + parameter E p; + type E = Integer; + end D; + + type E = Real; +end M; +---- + +To recursively instantiate `M` allowing the generation of flat equation system we have the following steps (not including checks): + +. Instantiate `M`, which partially instantiates `B`, `b`, `C`, `D`, and `E`. +. Instantiate `M.b`: + .. First find the class `B` in `M` (the partially instantiated elements have correct name allowing lookup) + .. Instantiate the partially instantiated `M.B` with the modifier `redeclare model A=D(p=1)`. + .. Partially instantiate `M.b.a` (no modifier), and `M.b.A` (with modifier `=D(p=1)`). +. Instantiate `M.b.a`: + .. First find the class `A` in `M.b` (the partially instantiated elements have correct name allowing lookup). + .. Instantiate the partially instantiated `M.b.A` with the modifier `=D(p=1)`. + ... Find the base class `=D` from the modifier. + This performs lookup for `D` in `M`, and finds the partially instantiated class `D`. + ... Instantiate the base class `M.D` with modifier `p=1`, and insert as unnamed node in `M.b.A`. + .... Partially instantiate the component `p` with modifier `=1`. + .... Find the base class `C` in `M.D`. + Since there is no local element called `C` the search is then continued in `M` and finds the partially instantiated class `M.C`. + .... Instantiate the base class `M.C` as below. +. Instantiate the base class `M.C` inserting the result into unnamed node in `M.b.a`: + .. Partially instantiate `e`. + .. Instantiate `e` which requires finding `E`. + First looking for `E` in the un-named node for `extends M.C`, and, since there is no local element `E` the search is then continued in `M` (which lexically encloses `M.C`) and finds `E` class inheriting from `Real`. + The `e` is then instantiated using class `E` inheriting from `Real`. +. Instantiate `M.b.a.p`: + .. First the class `E` in `M.b.a` finding `E` class inheriting from `Integer`. + .. Instantiate the `M.b.a.p` using the class `E` inheriting from `Integer` with modifier `=1`. + .. Instantiate the base class `Integer` with modifier `=1`, and insert as unnamed node in `M.b.a.p`. + +An implementation can use different heuristics to be more efficient by re-using instantiated elements as long as the resulting flat equation system is identical. + +Note that if `D` was consistently replaced by `A` in the example above the result would be identical (but harder to read due to two different classes called `A`). +==== + +==== Generation of the Flat Equation System + +During this process, all references by name in conditional declarations, modifications, dimension definitions, annotations, equations and algorithms are resolved to the real instance to which they are referring to, and the names are replaced by the global unique identifier of the instance. + +[NOTE] +This identifier is normally constructed from the names of the instances along a path in the instance tree (and omitting the unnamed nodes of `extends`-clauses), separated by dots. +Either the referenced instance belongs to the model to be simulated the path starts at the model itself, or if not, it starts at the unnamed root of the instance tree, e.g., in case of a constant in a package. + +[NOTE] +To resolve the names, a name lookup using the instance tree is performed, starting at the instance scope (unless the name is fully qualified) of the modification, algorithm or equation. +If it is not found locally the search is continued at the instance of the lexically enclosing class of the scope (this is normally not equal to the parent of the current instance), and then continued with their parents as described in <>. +If the found component is an outer declaration, the search is continued using the direct parents in the instance tree (see <>). +If the lookup has to look into a class which is not instantiated yet (or only partially instantiated), it is instantiated in place. + +The flat equation system consists of a list of variables with dimensions, flattened equations and algorithms, and a list of called functions which are flattened separately. +A flattened function consists of an algorithm or `external`-clause and top-level variables (variables directly declared in the function or one of its base classes) -- which recursively can contain other variables; the list of non-top-level variables is not needed. + +The instance tree is recursively walked through as follows for elements of the class (if necessary a partially instantiated component is first instantiated): + +* At each visited component instance, the name is inserted into the variables list. + Then the conditional declaration expression is evaluated if applicable. + + ** The variable list is updated with the actual instance + + ** The variability information and all other properties from the declaration are attached to this variable. + + ** Dimension information from the declaration and all enclosing instances are resolved and attached to the variable to define their complete dimension. + + ** If it is of record or simple type (`Boolean`, `Integer`, enumeration, `Real`, `String`, `Clock`, `ExternalObject`): + + *** In the modifications of _value_ attribute references are resolved using the instance scope of the modification. + An equation is formed from a reference to the name of the instance and the resolved modification value of the instance, and included into the equation system. + Except if the value for an element of a record is overridden by the value for an entire record; see <>. + + ** If it is of simple type (`Boolean`, `Integer`, enumeration, `Real`, `String`, `Clock`, `ExternalObject`): + + *** In the modifications of _non-value_ attributes, e.g., `start`, `fixed` etc. references are resolved using the instance scope of the modification. + An equation is formed from a reference to the name of the instance appended by a dot and the attribute name and the resolved modification value of the instance, and included into the equation system. + + ** If it is of a non-simple type the instance is recursively handled. + +* If there are equation or algorithm sections in the class definition of the instance, references are resolved using the instance scope of the instance and are included in the equation system. + Some references -- in particular to non simple, non record objects like connectors in `connect`-equations and states in `transition`-equations are not resolved yet and handled afterwards. + +* Instances of local classes are ignored. + +* The unnamed nodes corresponding to `extends`-clauses are recursively handled. + +* If there are function calls encountered during this process, the call is filled up with default arguments as defined in <>. + These are built from the modifications of input arguments which are resolved using their instance scope. + The called function itself is looked up in the instance tree. + All used functions are flattened and put into the list of functions. + +* Conditional components with false condition are removed afterwards and they are not part of the simulation model. ++ +[NOTE] +Thus, e.g., parameters don't need values in them. +However, type-error can be detected. + +* Each reference is checked, whether it is a valid reference, e.g., the referenced object belongs to or is an instance, where all existing conditional declaration expressions evaluate to true or it is a constant in a package. ++ +[NOTE] +Conditional components can be used in `connect`-equations, and if the component is conditionally disabled the `connect`-equation is removed. + +This leads to a flattened equation system, except for `connect`- and `transition`-equations. +These have to be transformed as described in <> and <>. +This may lead to further changes in the instance tree (e.g., from expandable connectors (<>)) and additional equations in the flattened equation system (e.g., connection equations (<>), generated equations for state machine semantics (<>)). + +[NOTE] +After flattening, the resulting equation system is self contained and covers all information needed to transform it to a simulatable model, but the class and instance trees are still needed: in the transformation process, there might be the need to instantiate further functions, e.g., from `derivative` annotation or from `inverse` annotation etc., on demand. \ No newline at end of file diff --git a/docs/6___interface.adoc b/docs/6___interface.adoc new file mode 100644 index 000000000..1759652e9 --- /dev/null +++ b/docs/6___interface.adoc @@ -0,0 +1,425 @@ +== Interface or Type Relationships +:id: interface-or-type-relationships + +A class or component, e.g., denoted `A`, can in some cases be used at a location designed for another class or component, e.g., denoted `B`. +In Modelica this is the case for replaceable classes (see <>) and for `inner`/`outer` elements (see <>). +Replaceable classes are the primary mechanism to create very flexible models. +In this chapter, the precise rules are defined when `A` can be used at a location designed for `B`. +The restrictions are defined in terms of compatibility rules (<> and <>) between "interfaces" (<>); this can also be viewed as sub-typing (<>). + +=== Interface Terminology +In this chapter, two kinds of terminology are used for identical concepts to get better understanding (e.g., by both engineers and computer scientists). +A short summary of the terms is given in the following table. +The details are defined in the rest of this chapter. + +Defintion Type or interface:: +The "essential" part of the public declaration sections of a class that is needed to decide whether `A` can be used instead of `B`. ++ +[NOTE] +E.g., a declaration `Real x` is part of the type (also called _interface_), but `import A` is not. + +Defintion Class type or inheritance interface:: +The "essential" part of the public _and protected_ declaration sections of a class that is needed to decide whether `A` can be used instead of `B`. +The class type, also called inheritance interface, is needed when inheritance takes place, since then the protected declarations have to be taken into account. + +Defintion Subtype or compatible interface:: +`A` is a subtype of `B`, or equivalently, the interface of `A` is compatible to the interface of `B`, if the "essential" part of the public declaration sections of `B` is also available in `A`. ++ +[NOTE] +E.g., if `B` has a declaration `Real x`, this declaration must also be present in `A`. +If `A` has a declaration `Real y`, this declaration may be present in `B`. ++ +If `A` is a subtype of `B`, then `B` is said to be a _supertype_ of `A`. + +Defintion Restricted subtype or plug compatible interface:: +`A` is a restricted subtype of `B`, or equivalently, the interface of `A` is plug compatible to the interface of `B`, if `A` is a subtype of `B` and if connector components in `A` that are not in `B`, are default connectable. ++ +[NOTE] +E.g., it is not allowed that these connectors have variables with the `input` prefix, because then they must be connected. ++ +A model or block `A` cannot be used instead of `B`, if the particular situation does not allow to make a connection to these additional connectors. +In such a case the stricter _plug compatible_ is required for a redeclaration. + +Defintion Function subtype or function compatible interface:: +`A` is a function subtype of `B`, or equivalently, the interface of `A` is function compatible to the interface of `B`, if `A` is a subtype of `B` and if the additional arguments of function `A` that are not in function `B` are defined in such a way, that `A` can be called at places where `B` is called. ++ +[NOTE] +E.g., an additional argument must have a default value. + +=== The Concepts of Type, Interface and Subtype +A _type_ can conceptually be viewed as a _set of values_. +When we say that the variable `x` has the type `Real`, we mean that the value of `x` belongs to the set of values represented by the type `Real`, i.e., roughly the set of floating point numbers representable by `Real`, for the moment ignoring the fact that `Real` is also viewed as a class with certain attributes. +Analogously, the variable `b` having `Boolean` type means that the value of `b` belongs to the set of values {`false`, `true`}. +The built-in types `Real`, `Integer`, `String`, `Boolean` are considered to be distinct types. + +The _subtype_ relation between types is analogous to the subset relation between sets. +A type `A1` being a subtype of type `A` means that the set of values corresponding to type `A1` is a subset of the set of values corresponding to type `A`. + +The type `Integer` is not a subtype of `Real` in Modelica even though the set of primitive integer values is a subset of the primitive real values since there are some attributes of `Real` that are not part of `Integer` (see <>). + +The concept of _interface_ as defined in <> and used in this document is equivalent to the notion of type based on sets in the following sense: + +An element is characterized by its interface defined by some attributes (see <>). +The _type_ of the element is the set of values having the same interface, i.e., the same attributes. + +A _subtype_ `A1` in relation to another type `A`, means that the elements of the set corresponding to `A1` is a subset of the set corresponding to `A`, characterized by the elements of that subset having additional properties. + +[example] +==== +Example: A record `R`: `record R Boolean b; Real x; end R;` + +Another record called `R2`: `record R2 Boolean b; Real x; Real y; end R2;` + +An instance `r`: `R r;` + +An instance `r2`: `R2 r2;` + +The type `R` of `r` can be viewed as the set of all record values having the attributes defined by the interface of `R`, e.g., the infinite set {`R(b=false, x=1.2)`, `R(b=false, x=3.4)`, `R(b=true, x=1.2)`, `R(b=true, x=1.2, y=2)`, `R(b=true, x=1.2, a=2)`, ...}. +The statement that `r` has the type (or interface) `R` means that the value of `r` belongs to this infinite set. + +The type `R2` is a subtype of `R` since its instances fulfill the additional property of having the component `Real y;` in all its values. + +.The type `R` can be defined as the set of record values containing `x` and `b`. The subtype `R2` is the subset of values that all contain `x`, `b`, and `y`. +image::media/subtype.svg[width=50%] +==== + +=== Interface or Type + +Based on a flattened class or component we can construct an interface for that flattened class or component. +The _interface_ or _type_ (the terms _interface_ and _type_ are equivalent and can be used interchangeably, and are different from _inheritance interface_ and _class type_) is defined as the following information about the flattened element itself: + +* Whether it is replaceable or not. +* Whether the class itself or the class of the component is transitively non-replaceable (see <>), and if not, the reference to the replaceable class it refers to. +* Whether it is a component or a class. +* Additional information about the element: +** The `flow` or `stream` prefix. +** Declared variability (`constant`, `parameter`, `discrete`). +** The prefixes `input` and `output`. +** The prefixes `inner` and/or `outer`. +** Whether the declaration is `final`, and in that case its semantics contents. +** Array sizes (if any). +** Condition of conditional components (if any). +** Which kind of specialized class. +** For an enumeration type or component of enumeration type the names of the enumeration literals in order. +** Whether it is a built-in type and the built-in type (`RealType`, `IntegerType`, `StringType` or `BooleanType`). +* Only for an `operator record` class and classes derived from `ExternalObject`: the full name of the operator record base class (i.e., the one containing the operations), or the derived class. +See <> and <>. ++ +The following item does not apply for an `operator record` class or class derived from `ExternalObject`, since the type is already uniquely defined by the full name. +* For each named public element of the class or component (including both local and inherited named elements) a tuple comprised of: +** Name of the element. +** Interface or type of the element. +[NOTE] +This might have been modified by modifiers and is thus not necessarily identical to the interface of the original declaration. + +The corresponding _constraining_ interface is constructed based on the _constraining_ type (see <>) of the declaration (if replaceable -- otherwise same as actual type) and with the _constraining_ interface for the named elements. + +In a class all references to elements of that class should be limited to their constraining interface. + +[NOTE] +The _constraining interface_ consists of only the public elements, and if the declaration is replaceable the element is limited to the constraining interface. + +[NOTE] +The public interface does not contain all of the information about the class or component. +When using a class as a base class we also need protected elements, and for internal type-checking we need, e.g., import-elements. +However, the information is sufficient for checking compatibility and for using the class to flatten components. + +==== Transitively Non-Replaceable +[NOTE] +In several cases it is important that no new elements can be added to the interface of a class, especially considering short class definitions. +Such classes are defined as _transitively non-replaceable_. + +A class reference is _transitively non-replaceable_ iff (i.e., if and only if) all parts of the name satisfy the following: + +* If the class definition is long it is transitively non-replaceable if not declared replaceable. +* If the class definition is short (i.e., `class A = P.B`) it is transitively non-replaceable if it is non-replaceable and equal to class reference (`P.B`) that is transitively non-replaceable. + +[NOTE] +According to <>, for a hierarchical name all parts of the name must be transitively non-replaceable, i.e., in `extends A.B.C` this implies that `A.B.C` must be transitively non-replaceable, as well as `A` and `A.B`, with the exception of the _class extends redeclaration mechanism_ see <>. + +==== Inheritance Interface or Class Type +For inheritance, the interface also must include protected elements; this is the only change compared to above. + +Based on a flattened class we can construct an _inheritance interface_ or _class type_ for that flattened class. +The inheritance interface or class type is defined as the following information about the flattened element itself: + +* Whether it is replaceable or not. +* Whether the class itself or the class of the component is transitively non-replaceable (see <>), and if not the reference to replaceable class it refers to. +* For each named element of the class (including both local and inherited named elements) a tuple comprised of: +** Name of the element. +** Whether the element is component or a class. +** For elements that are classes: Inheritance interface or class type of the element. +[NOTE] +This might have been modified by modifiers and is thus not necessarily identical to the interface of the original declaration. +** For elements that are components: interface or type of the element. +[NOTE] +This might have been modified by modifiers and is thus not necessarily identical to the interface of the original declaration. +* Additional information about the element: +** The `flow` or `stream` prefix. +** Declared variability (`constant`, `parameter`, `discrete`). +** The prefixes `input` and `output`. +** The prefixes `inner` and/or `outer`. +** Whether the declaration is `final`, and in that case its semantics contents. +** Array sizes (if any). +** Condition of conditional components (if any). +** Which kind of specialized class. +** For an enumeration type or component of enumeration type the names of the enumeration literals in order. +** Whether it is a built-in type and the built-in type (`RealType`, `IntegerType`, `StringType` or `BooleanType`). +** Visibility (`public` or `protected`). + +=== Interface Compatibility or Subtyping +An interface of a class or component `A` is compatible with an interface of a class or component `B` (or the constraining interface of `B`), or equivalently that the type of `A` is a subtype of the type of `B`, iff: + +* `A` is a class if and only if `B` is a class (and thus: `A` is a component if and only if `B` is a component). +* If `A` has an `operator record` base class then `B` must also have one and it must be the same. + If `A` does not have an operator record base class then `B` shall not have one. + See <>. +* If `A` is derived from `ExternalObject`, then `B` must also be derived from `ExternalObject` and have the same full name. + If `A` is not derived from `ExternalObject` then `B` shall not be derived from `ExternalObject`. + See <>. +* If `B` is not replaceable then `A` shall not be replaceable. +* If `B` is transitively non-replaceable then `A` must be transitively non-replaceable (see <>). + For all elements of the inheritance interface of `B` there must exist a compatible element with the same name and visibility in the inheritance interface of `A`. + The interface of `A` shall not contain any other elements. +[NOTE] +We might even extend this to say that `A` and `B` should have the same contents, as in the additional restrictions below. +* If `B` is replaceable then for all elements of the component interface of `B` there must exist a plug-compatible element with the same name in the component interface of `A`. +* If `B` is neither transitively non-replaceable nor replaceable then `A` must be linked to the same class, and for all elements of the component interface of `B` there must thus exist a plug-compatible element with the same name in the component interface of `A`. +* Additional restrictions on the additional information. + These elements should either match or have a natural total order: +** If `B` is a non-replaceable long class definition `A` must also be a long class definition. +** The `flow` or `stream` prefix should be matched for compatibility. +** Declared variability is ordered `constant` < `parameter` < `discrete` < continuous-time (`Real` without prefix), and `A` is only compatible with `B` if the declared variability in `A` is less than or equal the variability in `B`. +[NOTE] +For a redeclaration of an element the variability prefix is as default inherited by the redeclaration (i.e., no need to repeat `parameter` when redeclaring a parameter). +** The `input` and `output` prefixes must be matched. + This ensures that the rules regarding inputs/outputs for matching connectors and (non-connector inputs) are preserved, as well as the restriction on blocks. +[NOTE] +For a redeclaration of an element the `input` or `output` prefix is inherited from the original declaration. +** The `inner` and/or `outer` prefixes should be matched. +[NOTE] +For a redeclaration of an element the `inner` and/or `outer` prefixes are inherited from the original declaration (since it is not possible to have `inner` and/or `outer` as part of a redeclare). +** If `B` is final `A` must also be final and have the same semantic contents. +** The number of array dimensions in `A` and `B` must be matched. +** Conditional components are only compatible with conditional components. + The conditions must have equivalent contents (similar to array sizes, except there is no `:` for conditional components). +[NOTE] +For a redeclaration of an element the conditional part is inherited from the original. +** A `function` class is only compatible with a `function` class, a `package` class only compatible with a `package` class, a `connector` class only with a `connector` class, a `model` or `block` class only compatible with a `model` or `block` class, and a `type` or `record` class only compatible with a `type` or `record` class. +** If `B` is an enumeration type `A` must also be an enumeration type and vice versa. + If `B` is an enumeration type not defined as `:` then `A` must have the same enumeration literals in the same order; if `B` is an enumeration type defined as `:` then there is no restriction on the enumeration type `A`. +** If `B` is a built-in type then `A` must also be of the same built-in type and vice versa. + +[NOTE] +Intuitively, that the type `A` is a subtype of the type of `B` means that all important elements of `B` are present in `A`. + +Plug-compatibility is a further restriction of compatibility (subtyping) defined in <>, and further restricted for functions, see <>. +For a replaceable declaration or modifier the default class must be compatible with the constraining class. + +For a modifier the following must apply: + +* The modified element should exist in the element being modified. +* The modifier should be compatible with the element being modified, and in most cases also plug-compatible, see <>. + +[NOTE] +If the original constraining flat class is legal (no references to unknown elements and no illegal use of class/component), and modifiers legal as above, then the resulting flat class will be legal +(no references to unknown elements and no illegal use of class/component and compatible with original constraining class) and references refer to similar entities. + +=== Plug-Compatibility or Restricted Subtyping +[NOTE] +If a sub-component is redeclared, see <>, it is impossible to connect to any new connector. +A connector with `input` prefix must be connected to, and since one cannot connect across hierarchies, one should not be allowed to introduce such a connector at a level where a connection is not possible. +Therefore all public components present in the interface `A` that are not present in `B` must be connected by default. + +Definition Plug-compatibility (= restricted subtyping):: ++ +-- +An interface `A` is plug-compatible with (a restricted subtype of) an interface `B` (or the constraining interface of `B`) iff: + +* `A` is compatible with (subtype of) `B`. +* All public components present in `A` but not in `B` must be default-connectable (as defined below). +-- + +Definition Default connectable:: ++ +-- +A component of an interface is default-connectable iff: + +* All of its components are default connectable. +* A connector component must not be an `input`. +[NOTE] +Otherwise a connection to the input will be missing. +* A connector component must not be of an expandable connector class. +[NOTE] +The expandable connector does potentially have inputs. +* A parameter, constant, or non-connector input must either have a binding equation or all of its sub-components must have binding equations. +-- + +Based on the above definitions, there are the following restrictions: + +* A redeclaration of an inherited top-level component must be _compatible_ _with_ (subtype of) the constraining interface of the element being redeclared. +* In all other cases redeclarations must be _plug-compatible_ with the constraining interface of the element being redeclared. + +[NOTE] +The reason for the difference is that for an inherited top-level component it is possible to connect to the additional connectors, either in this class or in a derived class. + +[example] +==== +Example: + +[source,modelica] +---- +partial model TwoFlanges + Modelica.Mechanics.Rotational.Interfaces.Flange_a flange_a; + Modelica.Mechanics.Rotational.Interfaces.Flange_b flange_b; +end TwoFlanges; + +partial model FrictionElement + extends TwoFlanges; + ... +end FrictionElement; + +model Clutch "compatible - but not plug-compatible with FrictionElement" + Modelica.Blocks.Interfaces.RealInput pressure; + extends FrictionElement; + ... +end Clutch; + +model DriveLineBase + extends TwoFlanges; + Inertia J1; + replaceable FrictionElement friction; +equation + connect(flange_a, J1.flange_a); + connect(J1.flange_b, friction.flange_a); + connect(friction.flange_b, flange_b); +end DriveLineBase; + +model DriveLine + extends DriveLineBase(redeclare Clutch friction); + Constant const; +equation + connect(const.y, friction.pressure); + // Legal connection to new input connector. +end DriveLine; + +model UseDriveLine "illegal model" + DriveLineBase base(redeclare Clutch friction); + // Cannot connect to friction.pressure +end UseDriveLine; +---- + +If a subcomponent is redeclared, it is impossible to connect to any new connector. +Thus any new connectors must work without being connected, i.e., the default connection of flow variables. +That fails for inputs (and expandable connectors may contain inputs). +For parameters and non-connector inputs it would be possible to provide bindings in a derived class, but that would require hierarchical modifiers and it would be bad modeling practice that a hierarchical modifier must be used in order to make a model valid. +A replaceable class might be used as the class for a sub-component, therefore plug-compatibility is required not only for replaceable sub-components, but also for replaceable classes. +==== + +=== Function-Compatibility or Function-Subtyping for Functions + +[NOTE] +Functions may be called with either named or positional arguments, and thus both the name and order is significant. +If a function is redeclared, see <>, any new arguments must have defaults (and be at the end) in order to preserve the meaning of existing calls. + +[[function-compatibility,Defintion Function-compatibility]] +Definition Function-compatibility or function-subtyping for functions:: +A `function` class `A` is _function-compatible with or a function subtype of_ `function` class `B` iff (the terms _function-compatible_ and _function subtype_ of are synonyms and used interchangeably): ++ +* `A` is compatible to (subtype of) `B`. +* All public input components of `B` have correspondingly named public input components of `A` in the same order and preceding any additional public input components of `A`. +* All public output components of `B` have correspondingly named public output components of `A` in the same order and preceding any additional public output components of `A`. +* A public input component of `A` must have a binding assignment if the corresponding named element has a binding assignment in `B`. +* A public input component of `A` not present in `B` must have a binding assignment. +* If `A` is impure, then `B` must also be impure, compare <>. + +Based on the above definition the following restriction holds: + +* The interface of a redeclared function must be _function-compatible with or a function subtype of_ the constraining interface of the function being redeclared. + +Note that variability of function calls, see <>, cannot be determined using just the interface of a function, as the variabilities of default argument expressions are not expressed by the interface. +Hence a function redeclaration being function-compatible does not ensure that function calls will fulfill variability requirements, and tools must therefore check variability requirements separately. + +[example] +==== +Example: Demonstrating a redeclaration using a function-compatible function + +[source,modelica] +---- +function GravityInterface + input Modelica.Units.SI.Position position[3]; + output Modelica.Units.SI.Acceleration acceleration[3]; +end GravityInterface; + +function PointMassGravity + extends GravityInterface; + input Modelica.Units.SI.Mass m; +algorithm + acceleration := -Modelica.Constants.G*m*position/(position*position)^1.5; +end PointMassGravity; + +model Body + Modelica.Mechanics.MultiBody.Interface.Frame_a frame_a; + replaceable function gravity = GravityInterface; + constant Real failed[:] = gravity({1, 0, 0}); // May fail +equation + frame_a.f = gravity(frame_a.r0); + // or gravity(position = frame_a.r0); + frame_a.t = zeros(3); +end Body; + +model PlanetSimulation + parameter Modelica.Units.SI.Mass mSun = 2e30; + function sunGravity = PointMassGravity(m = mSun); + Body planet1(redeclare function gravity = sunGravity); + Body planet2(redeclare function gravity = PointMassGravity(m = 2e30)); + ... +end PlanetSimulation; +---- + +Note: `PointMassGravity` is not function-compatible with `GravityInterface` (no default for `m`!), but `sunGravity` inside `PlanetSimulation` is function-compatible with `GravityInterface`. + +The constant `failed` in `planet1`, will violate variability constraints, whereas it will work in `planet2`. +The call `gravity(frame_a.r0)` will work in both of them. +==== + +=== Type Compatible Expressions + +Certain expressions consist of an operator applied to two or more subexpressions (`A` and `B`). +This includes: + +* `if`-expressions, e.g., `if x then A else B`. +* Array expressions, e.g., `{A, B}` +* Binary operators if both operands are of simple types, e.g., `A + B`. + Binary operators for other types are only defined for operator records, see <>, and do not necessarily require that the operands are type compatible with each other. + +If the subexpressions satisfy the following restrictions they are called type compatible expressions. +Otherwise the expression is illegal. +The type of the full expression (e.g., `if x then A else B`) is also defined below. + +* If `A` is a record expression, `B` must also be a record expression with the same named elements. + In an expression that is not an array expression those elements must be type compatible. + In an array expression the two records may contain elements with different sizes, but apart from that they must be type compatible. + That generates a heterogenous array of records, see <>. + The type of the full expression is a record comprised of named elements that are type compatible with the corresponding named elements of both `A` and `B`. +* The rules for array expressions depend on the operation (the rules for binary operators are given in <> +and for array concatenation in <>). + The rules for the remaining case of `if`-expressions and array-expressions are: +** If `A` is an array expression then `B` must also be an array expression, and `ndims(A)` = `ndims(B)`. + The type of the full expression is an array expression with elements compatible with the elements of both `A` and `B`. + If both `size(A)` and `size(B)` are known and `size(A)` = `size(B)` then this defines the size of the full expression, otherwise the size of the full expression is not known until the expression is about to be evaluated. + In case of an `if`-expression the size of the full expression is defined based on the branch selected, and for other cases `size(A)` = `size(B)` must hold at this point. +** If `A` is a scalar expression of a simple type `B` must also be a scalar expression of a simple type. +* If `A` is a `Real` expression then `B` must be a `Real` or `Integer` expression. + The type of the full expression is `Real`, compare <>, unless the operator is a relational operator (<>) where the type of the full expression is `Boolean`. +* If `A` is an `Integer` expression then `B` must be a `Real` or `Integer` expression. + For exponentiation and division the type of the full expression is `Real` (even if both `A` and `B` are `Integer`) see <> and <>, for relational operators the type of the full expression is `Boolean`. + In other cases the type of the full expression is `Real` or `Integer` (same as `B`), compare <>. +* If `A` is a `Boolean` expression then `B` must be a `Boolean` expression and the type of the full expression is `Boolean`. +* If `A` is a `String` expression then `B` must be a `String` expression and the type of the full expression is `String`, unless the operator is a relational operator (<>) where the type of the full expression is `Boolean`. +* If `A` is an enumeration expression then `B` must be an enumeration expression and the type of the full expression is enumeration expression, unless the operator is a relational operator (<>) where the type of the full expression is `Boolean`. + The enumeration expressions must be defined in terms of an enumeration type with the same enumeration literals in the same order. +* For array and `if`-expressions, if `A` has an `operator record` base class then `B` must also have an `operator record` base class, and it must be the same, and otherwise neither `A` nor `B` may have an `operator record` base class. + This is also the `operator record` base class for the full expression, e.g., for `if (cond) then A else B`. +* If `A` is derived from `ExternalObject` then `B` must also be derived from `ExternalObject` and they must have the same full name; and otherwise neither `A` nor `B` may be derived from `ExternalObject`. + The common full name also defines the type of the full expression, e.g., for `if (cond) then A else B`. \ No newline at end of file diff --git a/docs/7___inheritance.adoc b/docs/7___inheritance.adoc new file mode 100644 index 000000000..ec70e0225 --- /dev/null +++ b/docs/7___inheritance.adoc @@ -0,0 +1,1338 @@ +== Inheritance, Modification, and Redeclaration +:id: inheritance-modification-and-redeclaration + +One of the major benefits of object-orientation is the ability to _extend_ the behavior and properties of an existing class. +The original class, known as the _base class_, is extended to create a more specialized version of that class, known as the _derived class_. +In this process, the data and behavior of the original class in the form of variable declarations, equations, and certain other contents are reused, or _inherited_, by the derived class. +In fact, the inherited contents is copied from the superclass into the derived class, but before copying certain operations, such as type expansion, checking, and modification, are performed on the inherited contents when appropriate. +This chapter describes the inheritance concept in Modelica, together with the related concepts modification and redeclaration. + +=== Inheritance -- Extends Clause + +The class `A` is called a _base class_ of `B`, if `B` extends `A`. +The converse relation is then expressed as `B` being a _derived class_ of `A`, or as `B` being _derived from_ `A`. +This relation is specified by an `extends`-clause in `B` or in one of `B`'s base classes. +A class inherits all elements from its base classes, and may modify all non-final elements inherited from base classes, as explained below. + +The `extends`-clause is used to specify inheritance from a base class into an (enclosing) class containing the `extends`-clause. +It is an unnamed element of a class definition that uses a name and an optional modification to specify a base class of the class defined using the class definition. The syntax of the `extends`-clause is as follows: + +[source,grammar] +---- +extends-clause : + extends name [ class-or-inheritance-modification ] [ annotation-clause ] +---- + +The name of the base class is looked up in the partially flattened enclosing class (see <>) of the `extends`-clause. +If the optional `class-or-inheritance-modification` contains any `inheritance-modification` the base class is then modified as described in <>. +The possibly modified found base class is flattened with a new environment and the partially flattened enclosing class of the `extends`-clause. +The new environment is the result of merging + +* arguments of all enclosing class environments that match names in the flattened base class +* a `class-modification` constructed from all `argument` of the `inheritance-modification` + +in that order. + +[example] +==== +Example: + +[source,modelica] +---- +class A + parameter Real a, b; +end A; + +class B + extends A(b = 2); +end B; + +class C + extends B(a = 1); +end C; +---- +==== + +The elements of the flattened base class become elements of the flattened enclosing class, and are added at the place of the `extends`-clause: specifically components and classes, the equation sections, algorithm sections, optional `external`-clause, and the contents of the annotation at the end of the class, but excluding `import`-clauses. + +[example] +==== +From the example above we get the following flattened class: + +[source,modelica] +---- +class Cinstance + parameter Real a = 1; + parameter Real b = 2; +end Cinstance; +---- + +The ordering of the merging rules ensures that, given classes `A` and `B` defined above, + +[source,modelica] +---- +class C2 + B bcomp(b = 3); +end C2; +---- + +yields an instance with `bcomp.b = 3`, which overrides `b = 2`. +==== + +The declaration elements of the flattened base class shall either: + +* Not already exist in the partially flattened enclosing class (i.e., have different names). +* The new element is a long form of redeclare or uses the `class extends A` syntax, see <>. +* Be exactly identical to any element of the flattened enclosing class with the same name and the same level of protection (public or protected) and same contents. + In this case, the first element in order (can be either inherited or local) is kept. + It is recommended to give a warning for this case; unless it can be guaranteed that the identical contents will behave in the same way. + +Otherwise the model is incorrect. + +[example] +==== +Clarifying order: + +[source,modelica] +---- +function A + input Real a; + input Real b; +end A; + +function B + extends A; + input Real a; +end B; +---- + +The inputs of `B` are `{a, b}` in that order; the `input Real a;` is ignored. +==== + +Equations of the flattened base class that are syntactically equivalent to equations in the flattened enclosing class are discarded. +This feature is deprecated, and it is recommended to give a warning when discarding them and for the future give a warning about all forms of equivalent equations due to inheritance. + +[NOTE] +Equations that are mathematically equivalent but not syntactically equivalent are not discarded, hence yield an overdetermined system of equations. + +==== Multiple Inheritance + +Multiple inheritance is possible since multiple `extends`-clauses can be present in a class. + +[NOTE] +As stated in <>, it is illegal for an `extends`-clause to influence the lookup of the class name of any `extends`-clause in the same class definition. + +==== Inheritance of Protected and Public Elements + +If an `extends`-clause is used under the `protected` heading, all elements of the base class become protected elements of the current class. +If an `extends`-clause is a public element, all elements of the base class are inherited with their own protection. +The eventual headings `protected` and `public` from the base class do not affect the consequent elements of the current class (i.e., headings `protected` and `public` are not inherited). + +==== Restrictions on the Kind of Base Class + +Since specialized classes of different kinds have different properties, see <>, only specialized classes that are _in some sense compatible_ to each other can be derived from each other via inheritance. +The following table shows which kind of specialized class can be used in an `extends`-clause of another kind of specialized class (the grey cells mark the few exceptional cases, where a specialized class can be derived from a specialized class of another kind): + +[cols="13*^",options="autowidth"] +|=== +h| 12+h|Base Class +.>h|Derived Class +h|[.vertical-text]#package# +h|[.vertical-text]#operator# +h|[.vertical-text]#function# +h|[.vertical-text]#operator function# +h|[.vertical-text]#type# +h|[.vertical-text]#record# +h|[.vertical-text]#operator record# +h|[.vertical-text]#expandable connector# +h|[.vertical-text]#connector# +h|[.vertical-text]#block# +h|[.vertical-text]#model# +h|[.vertical-text]#class# + +h|package |✓ | | | | | | | | | | h|✓ +h|operator | |✓ | | | | | | | | | h|✓ +h|function | | |✓ | | | | | | | | h|✓ +h|operator function | | h|✓ |✓ | | | | | | | h|✓ +h|type | | | | |✓ | | | | | | h|✓ +h|record | | | | | |✓ | | | | | h|✓ +h|operator record | | | | | | |✓ | | | | h|✓ +h|expandable connector | | | | | | | |✓ | | | h|✓ +h|connector | | | | h|✓ h|✓ h|✓ | |✓ | | h|✓ +h|block | | | | | h|✓ | | | |✓ | h|✓ +h|model | | | | | h|✓ | | | h|✓ |✓ h|✓ +h|class | | | | | | | | | | | |✓ +|=== + +If a derived class is inherited from another type of specialized class, then the result is a specialized class of the derived class type. + +[NOTE] +For example, if a `block` inherits from a `record`, then the result is a `block`. + +All specialized classes can be derived from `class`, provided that the resulting class fulfills the restriction of the specialized class. +A `class` may only contain class definitions, annotations, and `extends`-clauses (having any other contents is deprecated). + +[NOTE] +It is recommended to use the most specific specialized class. + +The specialized classes `package`, `operator`, `function`, `type`, `record`, `operator record`, and `expandable connector` can only be derived from their own kind and from `class`. + +[NOTE] +E.g., a package can only be base class for packages. +All other kinds of classes can use the `import`-clause to use the contents of a package. + +[example] +==== +Example: + +[source,modelica] +---- +record RecordA + ... +end RecordA; + +package PackageA + ... +end PackageA; + +package PackageB + extends PackageA; // fine +end PackageB; + +model ModelA + extends RecordA; // fine +end ModelA; + +model ModelB + extends PackageA; // error, inheritance not allowed +end ModelB; +---- +==== + +==== Require Transitively Non-Replaceable + +The class name used after `extends` for base classes and for constraining classes must use a class reference considered transitively non-replaceable, see definition in <>. +For a replaceable component declaration without `constraining-clause` the class must use a class reference considered transitively non-replaceable. + +[NOTE] +The requirement to use a transitively non-replaceable name excludes the long form of redeclare, i.e., `redeclare model extends M ...` where `M` must be an inherited replaceable class. + +[NOTE] +The rule for a replaceable component declaration without `constraining-clause` implies that constraining classes are always transitively non-replaceable -- both if explicitly given or implicitly by the declaration. + +=== Modifications + +A _modification_ is part of an element. +It modifies the instance generated by that element. +A modification contains _element modifications_ (e.g., `vcc(unit = "V") = 1000`) and _element-redeclarations_ (e.g., `redeclare type Voltage = Real(unit="V")`). + +There are three kinds of constructs in the Modelica language in which modifications can occur: + +* variable declarations +* short class definitions +* `extends`-clauses + +A modifier modifies one or more declarations (definitions) from a class by changing some aspect(s) of the declarations (definitions). +The most common kind of modifier just changes the _default value_ or the `start`-attribute in a binding equation; the value and/or `start`-attribute should be compatible with the variable according to <>. + +An _element modification_ overrides the declaration equation in the class used by the instance generated by the modified element. + +[example] +==== +Example: Modifying the default `start` value of the `altitude` variable: + +[source,modelica] +---- +Real altitude(start = 59404); +---- +==== + +A modification (e.g., `C1 c1(x = 5)`) is called a _modification equation_, if the modified variable (here: `c1.x`) is a non-parameter variable. + +[NOTE] +The modification equation is created, if the modified component (here: `c1`) is also created (see <>). +In most cases a modification equation for a non-parameter variable requires that the variable was declared with a declaration equation, see <>; in those cases the declaration equation is replaced by the modification equation. + +A more dramatic change is to modify the _type_ and/or the _prefixes_ and possibly the _dimension sizes_ of a declared element. +This kind of modification is called an _element-redeclaration_ (see <>) and requires the special keyword `redeclare` to be used in the modifier in order to reduce the risk for accidental modeling errors. +In most cases a declaration that can be redeclared must include the prefix `replaceable` (see <>). +The modifier value (and class for redeclarations) is found in the context in which the modifier occurs, see also <>. + +[example] +==== +Example: Scope for modifiers: + +[source,modelica] +---- +model B + parameter Real x; + package Medium = Modelica.Media.PartialMedium; +end B; + +model C + parameter Real x = 2; + package Medium = Modelica.Media.PartialMedium; + B b(x = x, redeclare package Medium = Medium); + // The 'x' and 'Medium' being modified are declared in the model B. + // The modifiers '= x' and '= Medium' are found in the model C. +end C; + +model D + parameter Real x = 3; + package Medium = Modelica.Media.PartialMedium; + C c(b(x = x, redeclare package Medium = Medium)); + // The 'x' and 'Medium' being modified are declared in the model B. + // The modifiers '= x' and '= Medium' are found in the model D. +end D; +---- +==== + +When present, the description-string of a modifier overrides the existing description. + +==== Syntax of Modifications and Redeclarations + +The syntax is defined in the grammar, see <>. + +==== Modification Environment + +The _modification environment_ of a class contains arguments which modify elements of the class (e.g., parameter changes) when the class is flattened. +The modification environment is built by merging class modifications, where outer modifications override inner modifications. + +[NOTE] +This should not be confused with `inner outer` prefixes described in <>. + +==== Merging of Modifications + +Merging of modifiers means that outer modifiers override inner modifiers. +The merging is hierarchical, and a value for an entire non-simple component overrides value modifiers for all components, and it is an error if this overrides a `final` prefix for a component, or if value for a simple component would override part of the value of a non-simple component. +When merging modifiers each modification keeps its own `each` prefix. + +[example] +==== +Example: The following larger example demonstrates several aspects: + +[source,modelica] +---- +class C1 + class C11 + parameter Real x; + end C11; +end C1; + +class C2 + class C21 + ... + end C21; +end C2; + +class C3 + extends C1; + C11 t(x = 3); // ok, C11 has been inherited from C1 + C21 u; // ok, even though C21 is inherited below + extends C2; +end C3; +---- +The modification environment of the declaration of `t` is `(x = 3)`. + +The following example demonstrates overriding part of non-simple component: +[source,modelica] +---- +record A + parameter Real x; + parameter Real y; +end A; + +model B + parameter A a = A(2, 3); +end B; + +model C + B b1(a(x = 4)); // Error: Cannot override value for a.x when a has a value. +end C; +---- + +The modification environment is built by merging class modifications, as shown by: +[source,modelica] +---- +class C1 + parameter Real a; +end C1; + +class C2 + parameter Real b; + parameter Real c; +end C2; + +class C3 + parameter Real x1; // No default value + parameter Real x2 = 2; // Default value 2 + parameter C1 x3; // No default value for x3.a + parameter C2 x4(b = 4); // x4.b has default value 4 + parameter C1 x5(a = 5); // x5.a has default value 5 + extends C1; // No default value for inherited element a + extends C2(b = 6, c = 77); // Inherited b has default value 6 +end C3; + +class C4 + extends C3(x2 = 22, x3(a = 33), x4(c = 44), x5 = x3, a = 55, b = 66); +end C4; +---- + +Outer modifications override inner modifications, e.g., `b = 66` overrides the nested class modification of extends `C2(b = 6)`. +This is known as merging of modifications: merge((b = 66), (b = 6)) becomes (b = 66). + +A flattening of class `C4` will give an object with the following variables: + +[cols="a,a",options=autowidth] +|=== +|Variable |Default value + +|`x1` |`none` +|`x2` |`22` +|`x3.a` |`33` +|`x4.b` |`4` +|`x4.c` |`44` +|`x5.a` |`x3.a` +|`a` |`55` +|`b` |`66` +|`c` |`77` +|=== +==== + +==== Single Modification + +Two arguments of a modification shall not modify the same element, attribute, or description-string. +When using qualified names the different qualified names starting with the same identifier are merged into one modifier. +This merged modifier can be described as a purely syntactic rewriting to an equivalent modifier, except in the case of replaceable redeclarations without a constraining type, see <>. +The latter is described in the example below. +If a modifier with a qualified name has the `each` or `final` prefix, that prefix is only seen as applied to the final part of the name. + +[example] +==== +Example: + +[source,modelica] +---- +class C1 + Real x[3]; +end C1; +class C2 = C1(x = ones(3), x = ones(3)); // Error: x designated twice +class C3 + class C4 + Real x; + end C4; + C4 a(final x.unit = "V", x.displayUnit = "mV", x = 5.0); + // Ok, different attributes designated (unit, displayUnit and value) + // identical to: + C4 b(x(final unit = "V", displayUnit = "mV") = 5.0)); + + C4 c(final x, final x.unit = "V", x.displayUnit = "mV"); + // OK, different attributes and "final x" in itself is OK, + // identical to (the final on unit is redundant): + C4 d(final x(final unit = "V", displayUnit = "mV")); +end C3; +---- + +The following examples are incorrect: +[source,modelica] +---- +m1(r = 1.5, r = 1.6) // Multiple modifier for r (its value) +m1(r = 1.5, r = 1.5) // Multiple modifier for r (its value) - even if identical +m1(r.start = 2, r(start = 3)) // Multiple modifier for r.start +m1(x.r = 1.5 "x", x.r(start = 2.0) "y")) // Multiple description-string for x.r +m1(r = R(), r(y = 2)) // Multiple modifier for r.y - both direct value and + // part of record +---- + +The following examples are correct: +[source,modelica] +---- +m1(r = 1.5, r(start = 2.0)) +m1(r = 1.6, r "x") +m1(r = R(), r(y(min = 2))) +---- + +Modifiers can be merged for non-replaceable redeclarations, or replaceable redeclarations with a constraining type, see <>. +[source,modelica] +---- +model Test + model A + replaceable Real x = 1; + end A; + + A a(redeclare Real x, x.start = 2); + // Identical to A a(redeclare Real x(start=2)); + A a(redeclare replaceable Real x constrainedby Real, x.start = 2); + // Identical to A a(redeclare Real x constrainedby Real(start=2)); +end Test; +---- + +For replaceable redeclarations without a constraining type the merging is not a local syntactic rewrite as it requires the constraining type, see <>. +[source,modelica] +---- +model Test + partial model Base + parameter Real p; + end Base; + + model Implementation + extends Base; + parameter Real q; + end Implementation; + + model A + replaceable Base b constrainedby Base(p=1); + end A; + + A a(redeclare replaceable Implementation b, b.q=1); + // This is treated the same as + // A a(redeclare replaceable Implementation b constrainedby Base(q=1)); + // This is no longer a local syntactic rewrite as the constrainedby + // references the constraining class +end Test; +---- +==== + +==== Modifiers for Array Elements + +The following rules apply to modifiers: + +* The `each` keyword on a modifier requires that it is applied in an array declaration/modification, and the modifier is applied individually to each element of the enclosing array (with regard to the position of `each`). + In case of nested modifiers this implies it is applied individually to each element of each element of the enclosing array; see example. + If the modified element is a vector and the modifier does not contain the `each` prefix, the modification is split such that the first element in the vector is applied to the first element of the vector of elements, the second to the second element, until the last element of the vector is applied to the last element of the array; it is an error if these sizes do not match. + Matrices and general arrays of elements are treated by viewing those as vectors of vectors etc. +* If a nested modifier is split, the split is propagated to all elements of the nested modifier, and if they are modified by the `each` keyword the split is inhibited for those elements. + If the nested modifier that is split in this way contains re-declarations that are split, it is illegal. + +[example] +==== +Example: + +[source,modelica] +---- +model C + parameter Real a[3]; + parameter Real d; +end C; + +model B + C c[5](each a = {1, 2, 3}, d = {1, 2, 3, 4, 5}); + parameter Real b = 0; +end B; +---- +This implies `c[i].a[j] = j` and `c[i].d = i`. + +[source,modelica] +---- +model D + B b(each c.a = {3, 4, 5}, c.d = {2, 3, 4, 5, 6}); + // Equivalent to: + B b2(c(each a = {3, 4, 5}, d = {2, 3, 4, 5, 6})); +end D; +---- +This implies `b.c[i].a[j] = 2+j` and `b.c[i].d = 1+i`. + +[source,modelica] +---- +model E + B b[2](each c(each a = {1, 2, 3}, d = {1, 2, 3, 4, 5}), p = {1, 2}); + // Without the first each one would have to use: + B b2[2](c(each a = {1, 2, 3}, d = fill({1, 2, 3, 4, 5}, 2)), p = {1, 2}); +end E; +---- +This implies `b[k].c[i].a[j] = j`, `b[k].c[i].d = i`, and `b[k].p = k`. +For `c.a` the additional (outer) `each` has no effect, but it is necessary for `c.d`. + +Specifying array dimensions after the type works the same as specifying them after the variable name. +[source,modelica] +---- +model F + Real fail1[2](each start = {1, 2}); // Illegal + Real work1[2](each start = 1); // Legal + Real[2] fail2(each start = {1, 2}); // Illegal + Real[2] work2(each start = 2); // Legal +end F; +---- +==== + +==== Final Element Modification Prevention + +An element defined as final by the `final` prefix in an element modification or declaration cannot be modified by a modification or by a redeclaration. +All elements of a final element are also final. + +[NOTE] +Setting the value of a parameter in an experiment environment is conceptually treated as a modification. +This implies that a final modification equation of a parameter cannot be changed in a simulation environment. + +[example] +==== +Example: Final component modification. + +[source,modelica] +---- +type Angle = + Real(final quantity = "Angle", final unit = "rad", displayUnit = "deg"); + +model TransferFunction + parameter Real b[:] = {1} "numerator coefficient vector"; + parameter Real a[:] = {1, 1} "denominator coefficient vector"; + ... +end TransferFunction; + +model PI "PI controller" + parameter Real k = 1 "gain"; + parameter Real T = 1 "time constant"; + TransferFunction tf(final b = k * {T, 1}, final a = {T, 0}); +end PI; + +model Test + PI c1(k = 2, T = 3); // fine, will indirectly change tf.b to 2 * {3, 1} + PI c2(tf(b = {1})); // error, b is declared as final +end Test; +---- +==== + +[example] +==== +Example: Final class declaration. + +[source,modelica] +---- +model Test2 + final model MyTF = TransferFunction(b = {1, 2}); + /* Equivalently: + final model MyTF = TransferFunction(final a, final b = {1, 2}); + */ + MyTF tf1; // fine + MyTF tf2(a = {1, 2}); // error, all elements in MyTF are final + model M = MyTF(a = {4}); // error, all elements in MyTF are final + model TFX + extends MyTF; // fine + Real foo = 1.0; + end TFX; + TFX tfx(foo = 2.0); // fine, foo is not from MyTF + TFX tfx2(a = {1, 3}); // error, all elements from MyTF are final + model TFX3 = TFX(a = {1, 4}); // error, all elements from MyTF are final +end Test2; +---- +==== + +==== Removing Modifiers - break + +Modifications may contain the special keyword `break` instead of an expression. +The intention of `break` is to remove the value. + +The modifiers using `break` are merged using the same rule as other modifications, and follow the same restrictions so they cannot override a final modifier. +During flattening of an instantiated model, remaining `break` modifications (i.e., the ones that are not further overriden) are treated as if the expression was missing. +The `break` modifier for a variable of a simple type can be applied to the value and/or to specific attributes. +Unless `final` was specified, it is possible to override even if no value is present, either because there was no expression originally or because `break` overrides another `break`. + +[NOTE] +In a dialog, a tool may hide the keyword `break` and show an empty input field, without the overriden modification. +It should also be possible to remove this modifier to restore the overriden modification. + +There are also other uses of the keyword `break`, but importantly it is not an expression and thus it cannot be used as a sub-expression. + +[example] +==== +Example: Remove unwanted defaults for parameters: + +[source,modelica] +---- +partial model PartialStraightPipe + parameter Real roughness = 2.5e-5 "Average height of surface asperities"; + parameter Real height_ab (unit = "m" ) = 0 "Height between a and b"; + ... +end PartialStraightPipe; + +model StaticPipe + extends PartialStraightPipe; + parameter Real p_a_start = system.p_start; + ... +end StaticPipe; + +model MyPipe "Without defaults" + extends StaticPipe( + p_a_start = break, + roughness = break, + height_ab = break); +end MyPipe; +---- + +Replace a given parameter value by an initial computation: + +[source,modelica] +---- +model A + parameter Real diameter = 1; + final parameter Real radius = diameter / 2; +end A; + +model B "Initial equation for diameter" + extends A( final diameter(fixed = false) = break ); + parameter Real square=2; +initial equation + // solving equation below for diameter + square = f(diameter); +end B; +---- + +Replace the value for an inherited variable with a value computed from an algorithm: + +[source,modelica] +---- +model A + Real x = 1; +end A; + +model B "Computing x instead" + extends A(final x=break); +algorithm + x := 0; + while ... + x := x + ...; + end while; +end B; +---- + +Note that this is only legal because the modifier is modifying an inherited declaration. +Due to <> it is not legal to construct the corresponding component declaration, `A a(x=break);`. +==== + +=== Redeclaration + +A `redeclare` construct in a modifier replaces the declaration of a local class or component with another declaration. +A `redeclare` construct as an element replaces the declaration of a local class or component with another declaration. +Both `redeclare` constructs work in the same way. +The `redeclare` construct as an element requires that the element is inherited, and cannot be combined with a modifier of the same element in the `extends`-clause. +For modifiers, the redeclare of classes uses the `short-class-definition` construct, which is a special case of normal class definitions and semantically behaves as the corresponding `class-definition`. + +A modifier with the keyword `replaceable` is automatically seen as being a `redeclare`. + +In redeclarations some parts of the original declaration is automatically inherited by the new declaration. +This is intended to make it easier to write declarations by not having to repeat common parts of the declarations, and does in particular apply to prefixes that must be identical. +The inheritance only applies to the declaration itself and not to elements of the declaration. + +The general rule is that if no prefix within one of the following groups is present in the new declaration the old prefixes of that kind are preserved. + +The groups that are valid for both classes and components: + +* `public`, `protected` +* `inner`, `outer` +* constraining type according to rules in <> + +The groups that are only valid for components: + +* `flow`, `stream` +* `discrete`, `parameter`, `constant` +* `input`, `output` +* array dimensions + +Note that if the old declaration was a short class definition with array dimensions the array dimensions are not automatically preserved, and thus have to be repeated in the few cases they are used. + +Replaceable component array declarations with array sizes on the left of the component are seen as syntactic sugar for having all arrays sizes on the right of the component; and thus can be redeclared in a consistent way. + +The presence of annotations on the `redeclare` construct in a modifier is deprecated, but since none of the annotations in the specification ever had a meaning in this context it only impacts vendor-specific annotations. + +[NOTE] +The inheritance is from the original declaration. +In most cases replaced or original does not matter. +It does matter if a user redeclares a variable to be a parameter and then redeclares it without parameter. + +[example] +==== +[source,modelica] +---- +model HeatExchanger + replaceable parameter GeometryRecord geometry; + replaceable input Real u[2]; +end HeatExchanger; + + HeatExchanger( + /*redeclare*/ replaceable /*parameter*/ GeoHorizontal geometry, + redeclare /*input*/ Modelica.Units.SI.Angle u /*[2]*/); + // The semantics ensure that parts in /*.*/ are automatically added + // from the declarations in HeatExchanger. +---- + +Example of arrays on the left of the component name: +[source,modelica] +---- +model M + replaceable Real [4] x[2]; + // Seen as syntactic sugar for "replaceable Real x[2, 4];" + // Note the order. +end M; +M m(redeclare Modelica.Units.SI.Length x[2, 4]); // Valid redeclare of the type +---- +==== + +==== The "class extends" Redeclaration Mechanism + +A class declaration of the type `redeclare class extends B(...)`, where `class` as usual can be replaced by any other specialized class, replaces the inherited class `B` with another declaration that extends the inherited class where the optional class-modification is applied to the inherited class. +Inherited `B` here means that the class containing `redeclare class extends B(...)` should also inherit another declaration of `B` from one of its `extends`-clauses. +The new declaration should explicitly include `redeclare`. + +[NOTE] +Since the rule about applying the optional class-modification implies that all declarations are inherited with modifications applied, there is no need to apply modifiers to the new declaration. + +For `redeclare class extends B(...)` the inherited class is subject to the same restrictions as a redeclare of the inherited element, and the original class `B` should be _replaceable_, and the new element is only replaceable if the new definition is replaceable. +In contrast to normal extends it is not subject to the restriction that `B` should be transitively non-replaceable (since `B` should be replaceable). + +The syntax rule for `class extends` construct is in the definition of the `class-specifier` nonterminal (see also class declarations in <>): + +[source,grammar] +---- +class-definition : + [ encapsulated ] class-prefixes + class-specifier + +class-specifier : long-class-specifier | ... + +long-class-specifier : ... + | extends IDENT [ class-modification ] description-string + composition end IDENT +---- + +The nonterminal `class-definition` is referenced in several places in the grammar, including the following case which is used in some examples below, including `package extends` and `model extends`: +[source,grammar] +---- +element : + import-clause | + extends-clause | + [ redeclare ] + [ final ] + [ inner ] [ outer ] + ( ( class-definition | component-clause) | + replaceable ( class-definition | component-clause) + [constraining-clause comment]) +---- + +[example] +==== +Example to extend from existing packages: + +[source,modelica] +---- +package PowerTrain // library from someone else + replaceable package GearBoxes + ... + end GearBoxes; +end PowerTrain; + +package MyPowerTrain + extends PowerTrain; // use all classes from PowerTrain + redeclare package extends GearBoxes // add classes to sublibrary + ... + end GearBoxes; +end MyPowerTrain; +---- + +Example for an advanced type of package structuring with constraining types: + +[source,modelica] +---- +partial package PartialMedium "Generic medium interface" + constant Integer nX "number of substances"; + replaceable partial model BaseProperties + Real X[nX]; + ... + end BaseProperties; + + replaceable partial function dynamicViscosity + input Real p; + output Real eta; + ... + end dynamicViscosity; +end PartialMedium; + +package MoistAir "Special type of medium" + extends PartialMedium(nX=2); + + redeclare model extends BaseProperties(T(stateSelect = StateSelect.prefer)) + // replaces BaseProperties by a new implementation and + // extends from Baseproperties with modification + // note, nX = 2 (!) + equation + X = {0, 1}; + ... + end BaseProperties; + + redeclare function extends dynamicViscosity + // replaces dynamicViscosity by a new implementation and + // extends from dynamicViscosity + algorithm + eta := 2 * p; + end dynamicViscosity; +end MoistAir; +---- + +Note, since `MostAir` extends from `PartialMedium`, constant `nX = 2` in package `MoistAir` and the model `BaseProperties` and the function `dynamicViscosity` is present in `MoistAir`. +By the following definitions, the available `BaseProperties` model is replaced by another implementation which extends from the `BaseProperties` model that has been temporarily constructed during the extends of package `MoistAir` from `PartialMedium`. +The redeclared `BaseProperties` model references constant `nX` which is 2, since by construction the redeclared `BaseProperties` model is in a package with `nX = 2`. + +This definition is compact but is difficult to understand. +At a first glance an alternative exists that is more straightforward and easier to understand: + +[source,modelica] +---- +package MoistAir2 "Alternative definition that does not work" + extends PartialMedium(nX=2, + redeclare model BaseProperties = MoistAir_BaseProperties, + redeclare function dynamicViscosity = MoistAir_dynamicViscosity); + + model MoistAir_BaseProperties + // wrong model since nX has no value + extends PartialMedium.BaseProperties; + equation + X = {1, 0}; + end MoistAir_BaseProperties; + + function MoistAir_dynamicViscosity + extends PartialMedium.dynamicViscosity; + algorithm + eta := p; + end MoistAir_dynamicViscosity; +end MoistAir2; +---- + +Here, the usual approach is used to extend (here from `PartialMedium`) and in the modifier perform all redeclarations. +In order to perform these redeclarations, corresponding implementations of all elements of `PartialMedium` have to be given under a different name, such as `MoistAir2.MoistAir_BaseProperties`, since the name `BaseProperties` already exists due to `extends PartialMedium`. +Then it is possible in the modifier to redeclare `PartialMedium.BaseProperties` to `MoistAir2.MoistAir_BaseProperties`. +Besides the drawback that the namespace is polluted by elements that have different names but the same implementation (e.g., `MoistAir2.BaseProperties` is identical to `MoistAir2.MoistAir_BaseProperties`) the whole construction does not work if arrays are present that depend on constants in `PartialMedium`, such as `X[nX]`: +The problem is that `MoistAir_BaseProperties` extends from `PartialMedium.BaseProperties` where the constant `nX` does not yet have a value. +This means that the dimension of array `X` is undefined and model `MoistAir_BaseProperties` is wrong. +With this construction, all constant definitions have to be repeated whenever these constants shall be used, especially in `MoistAir_BaseProperties` and `MoistAir_dynamicViscosity`. +For larger models this is not practical and therefore the only practically useful definition is the complicated construction in the previous example with `redeclare model extends BaseProperties`. + +To detect this issue the rule on lookup of composite names (see <>) ensures that `PartialMedium.dynamicViscosity` is incorrect in a simulation model. +==== + +==== Constraining Type +:id: constraining-type + +In a replaceable declaration the optional `constraining-clause` defines a constraining type. +Any modifications following the constraining type name are applied both for the purpose of defining the actual constraining type and they are automatically applied in the declaration and in any subsequent redeclaration. +The precedence order is that declaration modifiers override constraining type modifiers. + +If the `constraining-clause` is not present in the original declaration (i.e., the non-redeclared declaration): + +* The type of the declaration is also used as a constraining type. +* If modifiers are present in the original declaration, they also become modifiers on the constraining type. + +The syntax of a `constraining-clause` is as follows: + +[source,grammar] +---- +constraining-clause : + constrainedby name [ class-modification ] +---- + +[example] +==== +Example: Merging of modifiers + +[source,modelica] +---- +class A + parameter Real x; +end A; + +class B + parameter Real x = 3.14, y; // B is a subtype of A +end B; + +class C + replaceable A a(x = 1); +end C; + +class D + extends C(redeclare B a(y = 2)); +end D; +---- +which is equivalent to defining `D` as: +[source,modelica] +---- +class D + B a(x = 1, y = 2); +end D; +---- + +A modification of the constraining type is automatically applied in subsequent redeclarations: +[source,modelica] +---- +model ElectricalSource + replaceable SineSource source constrainedby MO(final n=5); + ... +end ElectricalSource; + +model TrapezoidalSource + extends ElectricalSource( + redeclare Trapezoidal source); // source.n=5 +end TrapezoidalSource; +---- + +A modification of the base type without a constraining type is automatically applied in subsequent redeclarations: +[source,modelica] +---- +model Circuit + replaceable model NonlinearResistor = Resistor(R=100); + ... +end Circuit; + +model Circuit2 + extends Circuit( + redeclare replaceable model NonlinearResistor + = ThermoResistor(T0 = 300)); + // As a result of the modification on the base type, + // the default value of R is 100 +end Circuit2; + +model Circuit3 + extends Circuit2( + redeclare replaceable model NonlinearResistor + = Resistor(R = 200)); + // The T0 modification is not applied because it did not + // appear in the original declaration +end Circuit3; +---- + +`Circuit2` is intended to illustrate that a user can still select any resistor model (including the original one, as is done in `Circuit3`), since the constraining type is kept from the original declaration if not specified in the redeclare. +Thus it is easy to select an advanced resistor model, without limiting the possible future changes. + +A redeclaration can redefine the constraining type: +[source,modelica] +---- +model Circuit4 + extends Circuit2( + redeclare replaceable model NonlinearResistor + = ThermoResistor constrainedby ThermoResistor); +end Circuit4; + +model Circuit5 + extends Circuit4( + redeclare replaceable model NonlinearResistor = Resistor); // illegal +end Circuit5; +---- +==== + +The class or type of component shall be a subtype of the constraining type. +In a redeclaration of a replaceable element, the class or type of a component must be a subtype of the constraining type. +The constraining type of a replaceable redeclaration must be a subtype of the constraining type of the declaration it redeclares. +In an element modification of a replaceable element, the modifications are applied both to the actual type and to the constraining type. + +In an element-redeclaration of a replaceable element the modifiers of the replaced constraining type are merged to both the new declaration and to the new constraining type, using the normal rules where outer modifiers override inner modifiers. + +When a class is flattened as a constraining type, the flattening of its replaceable elements will use the constraining type and not the actual default types. + +The number of dimensions in the constraining type should correspond to the number of dimensions in the type-part. +Similarly the type used in a redeclaration must have the same number of dimensions as the type of redeclared element. + +[example] +==== +Example: + +[source,modelica] +---- +replaceable T1 x[n] constrainedby T2; +replaceable type T=T1[n] constrainedby T2; +replaceable T1[n] x constrainedby T2; +---- +In these examples the number of dimensions must be the same in `T1` and `T2`, as well as in a redeclaration. +Normally `T1` and `T2` are scalar types, but both could also be defined as array types (with the same number of dimensions). +Thus if `T2` is a scalar type (e.g., `type T2 = Real;`) then `T1` must also be a scalar type, and if `T2` is defined as vector type (e.g., `type T2 = Real[3];`) then `T1` must also be vector type. +==== + +===== Constraining-Clause Annotations +:id: constraining-clause-annotations + +Description and annotations on the `constraining-clause` are applied to the entire declaration, and it is an error if they also appear on the definition. + +[NOTE] +The intent is that the description and/or annotation are at the end of the declaration, but it is not straightforward to specify this in the grammar. + +[example] +==== +Example: + +[source,modelica] +---- +replaceable model Load1 = + Resistor constrainedby TwoPin "The Load"; // Recommended +replaceable model Load2 = + Resistor "The Load" constrainedby TwoPin; // Identical to Load1 +replaceable model Load3 = + Resistor "The Load" constrainedby TwoPin "The Load"; // Error + +replaceable Resistor load1 + constrainedby TwoPin "The Load"; // Recommended +replaceable Resistor load2 + "The Load" constrainedby TwoPin; // Identical to load1 +replaceable Resistor load3 + "The Load" constrainedby TwoPin "The Load!"; // Error +---- +==== + +See also the examples in <>. + +==== Restrictions on Redeclarations +:id: restrictions-on-redeclarations + +The following additional constraints apply to redeclarations (after prefixes are inherited, see <>): + +* Only classes and components declared as replaceable can be redeclared with a new type, which must have an interface compatible with the constraining interface of the original declaration, and to allow further redeclarations one must use `redeclare replaceable`. ++ +[NOTE] +Redeclaration with the same type can be used to restrict variability and/or change array dimensions. +* An element declared as `constant` cannot be redeclared. +* An element declared as `final` shall not be modified, and thus not redeclared. +* Modelica does not allow a protected element to be redeclared as public, or a public element to be redeclared as protected. +* Array dimensions may be redeclared; provided the sub-typing rules in <> are satisfied. ++ +[NOTE] +This is one example of redeclaration of non-replaceable elements. + +==== Annotations for Redeclaration and Modification +:id: annotation-choices-for-suggested-redeclarations-and-modifications + +A declaration can have an annotation `choices` containing modifiers on `choice`, where each of them indicates a suitable redeclaration or modifications of the element. +This is a hint for users of the model, and can also be used by the user interface to suggest reasonable redeclarations, where the string comments on the `choice` modifiers can be used as textual explanations of the choices. +The annotation is not restricted to replaceable elements but can also be applied to non-replaceable elements, enumeration types, and simple variables. + +The string comments for the `choice` modifiers shall not automatically be copied to the modifier. + +The semantic restrictions in <> are not enforced for the `choice` modifiers. +For instance, several examples of using `redeclare` inside `choice(...)` will be given below. + +Lookup inside a `choice` modifier is performed in the context of the annotation, meaning that references may need to be transformed to preserve the meaning when a `choice` is applied in a different context. + +[NOTE] +It is recommended to avoid expressions with references to elements that are not globally accessible, such as contents within a `protected` section of a class. +By starting names with a dot it can be ensured that no transformation of references will be needed when a `choice` is applied, and that applicability of a `choice` does not depend on context, see <>. + +It is allowed to include choices that are invalid in some contexts, e.g., a value might violate a `min` attribute. +(Options for tools encountering such choices include not showing them, marking them as invalid, or detecting the violations later.) + +For a `Boolean` variable, a `choices` annotation may contain the annotation `checkBox = true`, meaning to display a checkbox to input the values `false` or `true` in the graphical user interface. + +The annotation `choicesAllMatching = true` on the following kinds of elements indicates that tools should automatically construct a menu with appropriate choices. + +* For a replaceable element the included elements should be usable for replacing it. + Exact criteria for inclusion in such a menu are not defined, but there shall be a way to at least get a selection of classes, `A.B....X.Z`, that are either directly or indirectly derived by inheritance from the constraining class of the declaration, where `A` to `X` are non-partial packages, and `Z` is non-partial. +* For a record variable the included elements shall include matching record constants and calls of matching record constructors (matching classes as for replaceable elements). + +This menu can be disabled using annotation `choicesAllMatching = false`. +It is possible to combine the two annotations for one declaration, and tools may avoid generating duplicate menu entries in that case. + +[NOTE] +When `choicesAllMatching` is not specified the following behavior is recommended for replaceable elements. +A tool could ideally present (at least) the same choices as for `choicesAllMatching = true`, but if it takes (too long) time to present the list it might be better to use the `choicesAllMatching = false` behavior instead. + +[example] +==== +Example: Demonstrating the `choices` and `choicesAllMatching = true` annotations applied to replaceable elements. + +[source,modelica] +---- +replaceable model MyResistor = Resistor + annotation(choices( + choice(redeclare model MyResistor=lib2.Resistor(a={2}) "..."), + choice(redeclare model MyResistor=lib2.Resistor2 "..."))); + +replaceable Resistor Load(R = 2) constrainedby TwoPin + annotation(choices( + choice(redeclare lib2.Resistor Load(a={2}) "..."), + choice(redeclare Capacitor Load(L=3) "..."))); + +replaceable FrictionFunction a(func = exp) constrainedby Friction + annotation(choices( + choice(redeclare ConstantFriction a(c=1) "..."), + choice(redeclare TableFriction a(table="...") "..."), + choice(redeclare FunctionFriction a(func=exp) "..."))); + +replaceable package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater + constrainedby Modelica.Media.Interfaces.PartialMedium + annotation(choicesAllMatching = true); +---- +==== + +[example] +==== +Example: Demonstrating the `choicesAllMatching = true` annotation for parameter records. + +[source,modelica] +---- +record Medium + parameter SI.Density rho "Density"; + ... +end Medium; + +record Air_30degC = Medium(rho = 1.149, ...); +constant Medium MyAir = Medium(rho = 1.1, ...); + +model OpenTank + parameter Medium medium = Medium() annotation(choicesAllMatching = true); +end OpenTank; +---- +The choices for `medium` shall include `Medium()`, `Air_30degC()`, and `MyAir`. +If `Medium()` is chosen it is necessary to also set its `rho` parameter. +==== + +[example] +==== +Example: Applying the `choices` annotation to nonreplaceable declarations, e.g., to describe enumerations. + +[source,modelica] +---- +type KindOfController = Integer(min = 1, max = 3) + annotation(choices( + choice = 1 "P", + choice = 2 "PI", + choice = 3 "PID")); + +model A + parameter KindOfController x; +end A; +A a(x = 3); +---- +Note that `"PID"` was not copied here. + +The `choices` annotation can also be applied to `Boolean` variables to define a check box. +[source,modelica] +---- +parameter Boolean useHeatPort = false annotation(choices(checkBox = true)); +---- +==== + +=== Selective Model Extension +:id: selective-model-extension + +[NOTE] +The goal of selective model extension is to enable unforeseen structural variability without requiring deliberately prepared base-models, Bürger (2019). +This is done by deselecting specific elements from a base class, described here, combined with adding elements as normal. + +Selective model extension is activated by using one (or more) `inheritance-modification` in the optional `class-or-inheritance-modification` of an `extends`-clause. + +[NOTE] +There is no corresponding mechanism for component modifications, short class definitions, or constrainedby. + +Consider a class `C` with an `extends` clause deselecting `D`: +[source,modelica] +---- +model C + extends B(..., break D, ...); + ... +end C; +---- +The semantic rules are: + +. The deselection `break D` is applied before any other, non selective model extension related, modifications of `B` in `C`. +. When adding elements from `B` to `C` the elements matched by any deselection in `extends B` are excluded. + * A component deselection, `break f`, matches the component with that name, `f`, of `B` and all connections with the component or its subcomponents. + Matched components must be models, blocks or connectors. + * A connection deselection, `break connect(a, b)`, matches all syntactical equivalent connections of `B`. + A connection `connect(c, d)`, with `c` and `d` arbitrary but valid connection arguments, is syntactically equivalent to a connection deselection `break connect(a, b)`, if, and only if, either, `c` is syntactically equivalent to `a` and `d` is syntactically equivalent to `b` or, vice versa, `c` is syntactically equivalent to `b` and `d` is syntactically equivalent to `a`. + Two code fragments `a` and `c` are syntactically equivalent, if, and only if, the context-free derivations of `a` and `c` according to the grammar given in <> are the same. +. Conditionally declared components of `B` are assumed to be declared for all purposes of matching. +. The deselected component may be of a partial class even in a simulation model. +. The deselection `break D` must match at least one element of `B`. +. The component deselection are applied before the connection deselections of the same `extends` clause. + +[example] +==== +Example: The following gives three typical use cases: adding a component *on a connection*, replacing a non-replaceable component, and finally constructing a reusable model from an example. + +[source,modelica] +---- +model System "An example model" + Plant plant; + BearingFriction friction; + Controller controller; + StepReference reference; +equation + connect(reference.y, controller.u_s); + connect(plant.y, controller.u_m); + connect(controller.y, plant.u); + connect(friction.flange_a, plant.flange_a); +end System; + +model FilterMeasurement "Component on a connection" + extends System(break connect(plant.y, controller.u_m)); + BesselFilter filter; +equation + connect(plant.y, filter.u); + connect(filter.y, controller.u_m); +end FilterMeasurement; + +model SampledControllerSystem "Replacing non-replaceable" + extends System(break controller); + SampledController controller; +equation + connect(reference.y, controller.u1); // Note: Different name + connect(plant.y, controller.u_m); + connect(controller.y, plant.u); +end FilterMeasurement; + +model NewPlant "Reusable model from example" + extends System(break controller, break reference); + RealInput u; + RealOutput y; +equation + connect(u, plant.u); + connect(plant.y, y); +end NewPlant; +---- +In these examples it would be possible to modify the `System` model instead, but in many cases that is not realistic. +For instance, it may not be possible to modify the `System` and the controlled system may be comprised of a large number of components in `System` -- instead of only two. +==== + +[NOTE] +-- +Some consequences of the rules are listed below: + +* The syntax ensures that nested components cannot be deselected. +* Deselected components cannot be modified, neither in the `extends` clause nor when using `C`. + However, `C` may add a component with same name as a deselected component (directly or through another `extends` clause) and that new component can be modified when using `C`. +* A class using selective model extension is not necessarily a sub-type of its base class. +* Deselection is designed to be light-weight in particular: + ** Deselection is independent of any modification. + ** What is deselected can be determined without considering any modifications, neither of the extending class `C` nor its base class `B`. + ** There is no need to instantiate any classes to know that some component is deselected (i.e., not there) for every possible instance of the model with the deselection. + An instance tree is not required. + ** Selective model extension operates on the syntactic level only. + ** Conditional components can be deselected without evaluating whether they are disabled or not. + In particular deselecting a disabled conditional component is not an error. + Connections involving the deselected conditional component are by the deselection removed as for a disabled component. + ** No need to check whether the class of the deselected component was partial. + ** Assuming the deselections are semantically valid they can be handled in any order. + Handling component deselections before connection deselections is only necessary to semantically check that a connection deselection does not involve a deselected component. +-- + +[example] +==== +Example: The syntactic equivalence of connection deselection ensures that connect-statements in for-loops can be deselected: + +[source,modelica] +---- +model B + ... +equation + if b then + for i in 2:10 loop + connect( // This comment does not impact syntactic equivalence. + a[i], + b[2*i] /* Without whitespace in the indexing expression. */ ); + end for; + else + for i in 20:30 loop + connect(b[i], a[2*i]); + end for; + end for; +end B; +model C + extends B(break connect(b[2 * i], a[i])); +end C; +---- + +In this case the deselection removes all of the connect-statements. +==== \ No newline at end of file diff --git a/docs/8___equations.adoc b/docs/8___equations.adoc new file mode 100644 index 000000000..dfe6c8a30 --- /dev/null +++ b/docs/8___equations.adoc @@ -0,0 +1,1072 @@ +== Equations +:id: equations + +An _equation_ is part of a class definition. +A scalar equation relates scalar variables, i.e., constrains the values that these variables can take simultaneously. +When latexmath:[n-1] variables of an equation containing latexmath:[n] variables are known, the value of the latexmath:[n]th variable can be inferred (solved for). +In contrast to an algorithm section, there is no order between the equations in an equation section and they can be solved separately. + +=== Equation Categories + +Equations in Modelica can be classified into different categories depending on the syntactic context in which they occur: + +* Normal equality equations occurring in equation sections, including `connect`-equations and other equation types of special syntactic form (see <>). +* Declaration equations, which are part of variable, parameter, or constant declarations (see <>). +* Modification equations, which are commonly used to modify attributes of classes (see <>). +* _Binding equations_, which include both declaration equations and element modification for the value of the variable itself. These are considered equations when appearing outside functions, and then a component with a binding equation has its value bound to some expression. (Binding equations can also appear in functions, see <>.) +* _Initial equations_, which are used to express equations for solving initialization problems (see <>). + +=== Flattening and Lookup in Equations + +A flattened equation is identical to the corresponding nonflattened equation. + +Names in an equation shall be found by looking up in the partially flattened enclosing class of the equation. + +=== Equations in Equation Sections + +An equation section is comprised of the keyword `equation` followed by a sequence of equations. +The formal syntax is as follows: + +[source,grammar] +---- +equation-section : + [ initial ] equation { some-equation ";" } +---- + +The following kinds of equations may occur in equation sections. +The syntax is defined as follows: + +[source,grammar] +---- +some-equation : + ( simple-expression "=" expression + | if-equation + | for-equation + | connect-equation + | when-equation + | component-reference function-call-args + ) + description +---- + +No statements are allowed in equation sections, including the assignment statement using the `:=` operator. + +==== Simple Equality Equations + +Simple equality equations are the traditional kinds of equations known from mathematics that express an equality relation between two expressions. +There are two syntactic forms of such equations in Modelica. +The first form below is _equality_ equations between two expressions, whereas the second form is used when calling a function with _several_ results. +The syntax for simple equality equations is as follows: + +[source,grammar] +---- +simple-expression "=" expression +---- + +The types of the left-hand-side and the right-hand-side of an equation need to be compatible in the same way as two arguments of binary operators (see <>). + +Three examples: + +* `simple_expr1 = expr2;` +* `(if pred then alt1 else alt2) = expr2;` +* `(out1, out2, out3) = function_name(inexpr1, inexpr2);` + +[NOTE] +According to the grammar the if-then-else expression in the second example needs to be enclosed in parentheses to avoid parsing ambiguities. +Also compare with <> about calling functions with several results in assignment statements. + +==== For-Equations - Repetitive Equation Structures + +The syntax of a `for`-equation is as follows: + +[source,grammar] +---- +for for-indices loop + { some-equation ";" } +end for ";" +---- + +A `for`-equation may optionally use several iterators (`for-indices`), see <> for more information: + +[source,grammar] +---- +for-indices: + for-index { "," for-index } + +for-index: + IDENT [ in expression ] +---- + +The following is one example of a prefix of a `for`-equation: + +[source,grammar] +---- +for IDENT in expression loop +---- + +===== Explicit Iteration Ranges of For-Equations + +The `expression` of a `for`-equation shall be a vector expression, where more general array expressions are treated as vector of vectors or vector of matrices. +It is evaluated once for each `for`-equation, and is evaluated in the scope immediately enclosing the `for`-equation. +The expression of a `for`-equation shall be evaluable. +The iteration range of a `for`-equation can also be specified as `Boolean` or as an enumeration type, see <> for more information. +The loop-variable (`IDENT`) is in scope inside the loop-construct and shall not be assigned to. +For each element of the evaluated vector expression, in the normal order, the loop-variable gets the value of that element and that is used to evaluate the body of the `for`-loop. + +[example] +==== +Example: + +[source,modelica] +---- +for i in 1 : 10 loop // i takes the values 1, 2, 3, ..., 10 +for r in 1.0 : 1.5 : 5.5 loop // r takes the values 1.0, 2.5, 4.0, 5.5 +for i in {1, 3, 6, 7} loop // i takes the values 1, 3, 6, 7 +for i in TwoEnums loop // i takes the values TwoEnums.one, TwoEnums.two + // for TwoEnums = enumeration(one, two) +---- + +The loop-variable may hide other variables as in the following example. +Using another name for the loop-variable is, however, strongly recommended. + +[source,modelica] +---- + constant Integer j = 4; + Real x[j] +equation + for j in 1:j loop // j takes the values 1, 2, 3, 4 + x[j] = j; // Uses the loop-variable j + end for; +---- +==== + +===== Implicit Iteration Ranges of For-Equations + +The iteration range of a loop-variable may sometimes be inferred from its use as an array index. +See <> for more information. + +[example] +==== +Example: + +[source,modelica] +---- + Real x[n], y[n]; +equation + for i loop // Same as: for i in 1:size(x, 1) loop + x[i] = 2 * y[i]; + end for; +---- +==== + +==== Connect-Equations + +A `connect`-equation has the following syntax: + +[source,grammar] +---- +connect "(" component-reference "," component-reference ")" ";" +---- + +These can be placed inside `for`-equations and `if`-equations; provided the indices of the `for`-loop and conditions of the `if`-equation are evaluable expressions that do not depend on `cardinality`, `rooted`, `Connections.rooted`, or `Connections.isRoot`. +The `for`-equations/`if`-equations are expanded. +`connect`-equations are described in detail in <>. + +The same restrictions apply to `Connections.branch`, `Connections.root`, and `Connections.potentialRoot`; which after expansion are handled according to <>. + +==== If-Equations + +The `if`-equations have the following syntax: + +[source,grammar] +---- +if expression then + { some-equation ";" } +{ elseif expression then + { some-equation ";" } +} +[ else + { some-equation ";" } +] +end if ";" +---- + +The `expression` of an `if`- or `elseif`-clause must be a scalar `Boolean` expression. +One `if`-clause, and zero or more `elseif`-clauses, and an optional `else`-clause together form a list of branches. +One or zero of the bodies of these `if`-, `elseif`- and `else`-clauses is selected, by evaluating the conditions of the `if`- and `elseif`-clauses sequentially until a condition that evaluates to true is found. +If none of the conditions evaluate to true the body of the `else`-clause is selected (if an `else`-clause exists, otherwise no body is selected). +In an equation section, the equations in the body are seen as equations that must be satisfied. +The bodies that are not selected have no effect on that model evaluation. + +The `if`-equations in equation sections which do not have exclusively parameter expressions as switching conditions shall have the same number of equations in each branch (a missing else is counted as zero equations and the number of equations is defined after expanding the equations to scalar equations). + +[NOTE] +If this condition is violated, the single assignment rule would not hold, because the number of equations may change during simulation although the number of unknowns remains the same. + +==== When-Equations + +The `when`-equations have the following syntax: + +[source,grammar] +---- +when expression then + { some-equation ";" } +{ elsewhen expression then + { some-equation ";" } +} +end when ";" +---- + +The `expression` of a `when`-equation shall be a discrete-time `Boolean` scalar or vector expression. +If `expression` is a clocked expression, the equation is referred to as a _clocked when-clause_ (see <>) rather than a `when`-equation, and is handled differently. +The equations within a `when`-equation are activated only at the instant when the scalar expression or any of the elements of the vector expression becomes true. + +[example] +==== +Example: The order between the equations in a `when`-equation does not matter, e.g.: + +[source,modelica] +---- +equation + when x > 2 then + y3 = 2*x + y1 + y2; // Order of y1 and y3 equations does not matter + y1 = sin(x); + end when; + y2 = sin(y1); +---- +==== + +===== Defining When-Equations by If-Expressions in Equality Equations + +A `when`-equation: + +[source,modelica] +---- +equation + when x > 2 then + v1 = expr1; + v2 = expr2; + end when; +---- + +is conceptually equivalent to the following equations containing special `if`-expressions + +[source,modelica] +---- +// Not correct Modelica +Boolean b(start = x.start > 2); +equation + b = x > 2; + v1 = if edge(b) then expr1 else pre(v1); + v2 = if edge(b) then expr2 else pre(v2); +---- + +[example] +==== +The equivalence is conceptual since `pre(...)` of a non discrete-time `Real` variable or expression can only be used within a `when`-clause. Example: + +[source,modelica] +---- + /* discrete */ Real x; + input Real u; + output Real y; +equation + when sample() then + x = a * pre(x) + b * pre(u); + end when; + y = x; +---- + +Here, `x` is a discrete-time variable (whether it is declared with the `discrete` prefix or not), but `u` and `y` cannot be discrete-time variables +(since they are not assigned in `when`-clauses). +However, `pre(u)` is legal within the `when`-clause, since the body of the `when`-clause is only evaluated at events, and thus all expressions are discrete-time expressions. +==== + +The start values of the introduced `Boolean` variables are defined by the taking the start value of the when-condition, as above where `b` is a parameter variable. +The start value of the special functions `initial`, `terminal`, and `sample` is `false`. + +===== Where a When-Equation May Occur + +* `when`-equations shall not occur inside initial equations. +* `when`-equations cannot be nested. +* `when`-equations can only occur within `if`-equations and `for`-equations if the controlling expressions are exclusively parameter expressions. + +[example] +==== +Example: The following `when`-equation is invalid: + +[source,modelica] +---- +when x > 2 then + when y1 > 3 then + y2 = sin(x); + end when; +end when; +---- +==== + +===== Equations within When-Equations + +The equations within the `when`-equation must have one of the following forms: + +* `v = expr;` +* `(out1, out2, out3, ...) = function_call_name(in1, in2, ...);` +* Operators `assert`, `terminate`, `reinit`. +* The `for`- and `if`-equations if the equations within the `for`- and `if`-equations satisfy these requirements. + +Additionally, + +* The different branches of `when`/`elsewhen` must have the same set of component references on the left-hand side. Here, the destination variable of a `reinit` (including when inside a `when`-clause activated with `initial()`) is not considered a left-hand side, and hence `reinit` is unaffected by this requirement (as are `assert` and `terminate`). +* The branches of an `if`-equation inside `when`-equations must have the same set of component references on the left-hand side, unless all switching conditions of the `if`-equation are parameter expressions. +* Any left hand side reference, (`v`, `out1`, ...), in a `when`-clause must be a component reference, and any indices must be parameter expressions. + +[example] +==== +The needed restrictions on equations within a `when`-equation becomes apparent with the following example: + +[source,modelica] +---- + Real x, y; +equation + x + y = 5; + when condition then + 2 * x + y = 7; // error: not valid Modelica + end when; +---- + +When the equations of the `when`-equation are not activated it is not clear which variable to hold constant, either `x` or `y`. +A corrected version of this example is: + +[source,modelica] +---- + Real x,y; +equation + x + y = 5; + when condition then + y = 7 - 2 * x; // fine + end when; +---- + +Here, variable `y` is held constant when the `when`-equation is deactivated and `x` is computed from the first equation using the value of `y` from the previous event instant. +Note that during event iterations `y` will be solved from a system of two equations. +==== + +[example] +==== +Example: The restrictions for `if`-equations mean that both of the following variants are illegal: + +[source,modelica] +---- + Real x, y; +equation + if time < 1 then + when sample(1, 2) then + x = time; + end when; + else + when sample(1, 3) then + y = time; + end when; + end if; + + when sample(1, 2) then + if time < 1 then + y = time; + else + x = time; + end if; + end when; +---- + +whereas the restriction to parameter-expression is intended to allow: + +[source,modelica] +---- + parameter Boolean b = true; + parameter Integer n = 3; + Real x[n]; +equation + if b then + for i in 1 : n loop + when sample(i, i) then + x[i] = time; + end when; + end for; + end if; +---- +==== + +===== Single Assignment Rule Applied to When-Equations + +The Modelica single-assignment rule (see <>) has implications for `when`-equations: + +* Two `when`-equations shall _not_ define the same variable. ++ +[NOTE] +-- +Without this rule this may actually happen for the erroneous model `DoubleWhenConflict` below, since there are two equations (`close = true; close = false;`) defining the same variable `close`. +A conflict between the equations will occur if both conditions would become `true` at the same time instant. + +[source,modelica] +---- +model DoubleWhenConflict + Boolean close; // Erroneous model: close defined by two equations! +equation + ... + when condition1 then + ... + close = true; + end when; + when condition2 then + close = false; + end when; + ... +end DoubleWhenConflict; +---- + +One way to resolve the conflict would be to give one of the two `when`-equations higher priority. +This is possible by rewriting the `when`-equation using `elsewhen`, as in the `WhenPriority` model below or using the statement version of the `when`-construct, see <>. +-- + +* A `when`-equation involving elsewhen-parts can be used to resolve assignment conflicts since the first of the when/elsewhen parts are given higher priority than later ones: ++ +[NOTE] +-- +Below it is well defined what happens if both conditions become `true` at the same time instant since `condition1` with associated conditional equations has a higher priority than `condition2`. + +[source,modelica] +---- +model WhenPriority + Boolean close; // Correct model: close defined by two equations! +equation + ... + when condition1 then + close = true; + elsewhen condition2 then + close = false; + end when; + ... +end WhenPriority; +---- + +An alternative to `elsewhen` (in an equation or algorithm) is to use an algorithm with multiple `when`-statements. +However, both statements will be executed if both conditions become `true` at the same time. +Therefore they must be in reverse order to preserve the priority, and any side-effect would require more care. + +[source,modelica] +---- +model WhenPriorityAlg + Boolean close; // Correct model: close defined by two when-statements! +algorithm + ... + when condition2 then + close := false; + end when; + when condition1 then + close := true; + end when; + ... +end WhenPriorityAlg; +---- +-- + +==== reinit + +`reinit` can only be used in the body of a `when`-equation. + It has the following syntax: + +[source,modelica] +---- +reinit(x, expr); +---- + +The operator reinitializes `x` with `expr` at an event instant. +`x` is a component-reference (where any subscripts are evaluable) referring to a `Real` variable (or an array of `Real` variables) that must be selected as a state (resp., states), i.e., `reinit` on `x` implies `stateSelect = StateSelect.always` on `x`. +`expr` needs to be type-compatible with `x`. +For any given variable (possibly an array variable), `reinit` can only be applied (either to an individual variable or to a part of an array variable) in one `when`-equation (applying `reinit` to a variable in several `when`- or `elsewhen`-clauses of the same `when`-equation is allowed). +If there are multiple `reinit` for a variable inside the same `when`- or `elsewhen`-clause, they must appear in different branches of an `if`-equation (in order that at most one `reinit` for the variable is active at any event). +In case of `reinit` active during initialization (due to `when initial()`), see <>. + +`reinit` does not break the single assignment rule, because `reinit(x, expr)` in equations evaluates `expr` to a value, then at the end of the current event iteration step it assigns this value to `x` (this copying from values to reinitialized state(s) is done after all other evaluations of the model and before copying `x` to `pre(x)`). + +[example] +==== +Example: If a higher index system is present, i.e., constraints between state variables, some state variables need to be redefined to non-state variables. +During simulation, non-state variables should be chosen in such a way that variables with an applied `reinit` are selected as states at least when the corresponding `when`-clauses become active. +If this is not possible, an error occurs, since otherwise `reinit` would be applied to a non-state variable. + +Example for the usage of `reinit` (bouncing ball): + +[source,modelica] +---- +der(h) = v; +der(v) = if flying then -g else 0; +flying = not (h <= 0 and v <= 0); +when h < 0 then + reinit(v, -e * pre(v)); +end when +---- +==== + +==== assert + +An equation or statement of one of the following forms is an assertion: + +[source,modelica] +---- +assert(condition, message); // Uses level=AssertionLevel.error +assert(condition, message, assertionLevel); +assert(condition, message, level = assertionLevel); +---- + +Here, `condition` is a `Boolean` expression, `message` is a `String` expression, and `assertionLevel` is an optional evaluable expression of the built-in enumeration type `AssertionLevel`. +It can be used in equation sections or algorithm sections. + +[NOTE] +This means that `assert` can be called as if it were a function with three formal parameters, the third formal parameter has the name `level` and the default value `AssertionLevel.error`. + +If the `condition` of an assertion is true, `message` is not evaluated and the procedure call is ignored. +If the `condition` evaluates to false, different actions are taken depending on the `level` input: + +* `level = AssertionLevel.error`: The current evaluation is aborted. The simulation may continue with another evaluation. If the simulation is aborted, `message` indicates the cause of the error. ++ +[NOTE] +Ways to continue simulation with another evaluation include using a shorter step-size, or changing the values of iterationvariables. ++ +Failed assertions take precedence over successful termination, such that if the model first triggers the end of successful analysis by reaching the stop-time or explicitly with `terminate`, but the evaluation with `terminal()=true` triggers an assert, the analysis failed. + +* `level = AssertionLevel.warning`: The current evaluation is not aborted. `message` indicates the cause of the warning. ++ +[NOTE] +It is recommended to report the warning only once when the condition becomes false, and it is reported that the condition is no longer violated when the condition returns to true. +The `assert`-statement shall have no influence on the behavior of the model. +For example, by evaluating the condition and reporting the message only after accepted integrator steps. +`condition` needs to be implicitly treated with `noEvent` since otherwise events might be triggered that can lead to slightly changed simulation results. + +Tools are recommended to provide more information than just the given message of a failed assertion, in particular the condition and the values of variables used in it. + +[NOTE] +-- +The `AssertionLevel.error` case can be used to avoid evaluating a model outside its limits of validity; for instance, a function to compute the saturated liquid temperature cannot be called with a pressure lower than the triple point value. + +The `AssertionLevel.warning` case can be used when the boundary of validity is not hard: for instance, a fluid property model based on a polynomial interpolation curve might give accurate results between temperatures of 250 K and 400 K, but still give reasonable results in the range 200 K and 500 K. +When the temperature gets out of the smaller interval, but still stays in the largest one, the user should be warned, but the simulation should continue without any further action. +The corresponding code would be: + +[source,modelica] +---- +assert(T > 250 and T < 400, "Medium model outside full accuracy range", + AssertionLevel.warning); +assert(T > 200 and T < 500, "Medium model outside feasible region"); +---- + +It is recommended that asserts have a simple message as above, formulated with the recommended tool behavior in mind. +Writing `assert(T<500, "Temperature = "\+String(T)\+" was above 500")` is thus not recommended, and is likely to lead to duplicated information. +-- + +==== terminate + +The `terminate`-equation or statement (using function syntax) successfully terminates the analysis which was carried out, see also <>. +The termination is not immediate at the place where it is defined since not all variable results might be available that are necessary for a successful stop. +Instead, the termination actually takes place when the current integrator step is successfully finalized or at an event instant after the event handling has been completed before restarting the integration. + +`terminate` takes a string argument indicating the reason for the success. + +[example] +==== +Example: The intention of `terminate` is to give more complex stopping criteria than a fixed point in time: + +[source,modelica] +---- +model ThrowingBall + Real x(start = 0); + Real y(start = 1); +equation + der(x) = ...; + der(y) = ...; +algorithm + when y < 0 then + terminate("The ball touches the ground"); + end when; +end ThrowingBall; +---- +==== + +==== Equation Operators for Overconstrained Connection-Based Equation Systems + +See <> for a description of this topic. + +=== Synchronous Data-Flow Principle and Single Assignment Rule + +Modelica is based on the synchronous data flow principle and the single assignment rule, which are defined in the following way: + +. Discrete-time variables keep their values until these variables are explicitly changed. Differentiated variables have `der(x)` corresponding to the time-derivative of `x`, and `x` is continuous, except when `reinit` is triggered, see <>. Variable values can be accessed at any time instant during continuous integration and at event instants. +. At every time instant, during continuous integration and at event instants, the equations express relations between variables which have to be fulfilled concurrently. +. Computation and communication at an event instant does not take time. ++ +[NOTE] +If computation or communication time has to be simulated, this property has to be explicitly modeled. +. There must exist a perfect matching of variables to equations after flattening, where a variable can only be matched to equations that can contribute to solving for the variable (_perfect matching rule_ -- previously called _single assignment rule_; see also globally balanced <>). + +=== Events and Synchronization + +An _event_ is something that occurs instantaneously at a specific time or when a specific condition occurs. +Events are for example defined by the condition occurring in a `when`-clause, `if`-equation, or `if`-expression. + +The integration is halted and an event occurs whenever an event generation expression, e.g., `x > 2` or `floor(x)`, changes its value. +An event generating expression has an internal buffer, and the value of the expression can only be changed at event instants. +If the evaluated expression is inconsistent with the buffer, that will trigger an event and the buffer will be updated with a new value at the event instant. +During continuous integration event generation expression has the constant value of the expression from the last event instant. + +[NOTE] +A root finding mechanism is needed which determines a small time interval in which the expression changes its value; the event occurs at the right side of this interval. + +[example] +==== +Example: + +[source,modelica] +---- +y = if u > uMax then uMax else if u < uMin then uMin else u; +---- + +During continuous integration always the same `if`-branch is evaluated. +The integration is halted whenever `u-uMax` or `u-uMin` crosses zero. +At the event instant, the correct `if`-branch is selected and the integration is restarted. + +Numerical integration methods of order $n$ ($n \geq 1$) require continuous model equations which are differentiable up to order $n$. +This requirement can be fulfilled if `Real` elementary relations are not treated literally but as defined above, because discontinuous changes can only occur at event instants and no longer during continuous integration. +==== + +[NOTE] +-- +It is a quality of implementation issue that the following special relations + +[source,modelica] +---- +time >= discrete expression +time < discrete expression +---- + +trigger a time event at `time = discrete expression`, i.e., the event instant is known in advance and no iteration is needed to find the exact event instant. +-- + +Relations are taken literally also during continuous integration, if the relation or the expression in which the relation is present, are the argument of `noEvent`. +`smooth` also allows relations used as argument to be taken literally. +The `noEvent` feature is propagated to all subrelations in the scope of the `noEvent` application. +For `smooth` the liberty to not allow literal evaluation is propagated to all subrelations, but the smoothness property itself is not propagated. + +[example] +==== +Example: + +[source,modelica] +---- +x = if noEvent(u > uMax) then uMax elseif noEvent(u < uMin) then uMin else u; +y = noEvent( if u > uMax then uMax elseif u < uMin then uMin else u); +z = smooth(0, if u > uMax then uMax elseif u < uMin then uMin else u); +---- + +In this case `x = y = z`, but a tool might generate events for `z`. +The `if`-expression is taken literally without inducing state events. + +The `smooth` operator is useful, if, e.g., the modeler can guarantee that the used `if`-expressions fulfill at least the continuity requirement of integrators. +In this case the simulation speed is improved, since no state event iterations occur during integration. +The `noEvent` operator is used to guard against _outside domain_ errors, e.g., `y = if noEvent(x >= 0) then sqrt(x) else 0.` +==== + +All equations and assignment statements within `when`-clauses and all assignment statements within `function` classes are implicitly treated with `noEvent`, i.e., relations within the scope of these operators never induce state or time events. + +[NOTE] +Using state events in `when`-clauses is unnecessary because the body of a `when`-clause is not evaluated during continuous integration. + +[example] +==== +Example: Two different errors caused by non-discrete-time expressions: + +[source,modelica] +---- +when noEvent(x1 > 1) or x2 > 10 then // When-condition must be discrete-time + close = true; +end when; +above1 = noEvent(x1 > 1); // Boolean equation must be discrete-time +---- + +The when-condition rule is stated in <>, and the rule for a non-`Real` equation is stated in <>. +==== + +Modelica is based on the synchronous data flow principle (see <>). + +[example] +==== +The rules for the synchronous data flow principle guarantee that variables are always defined by a unique set of equations. +It is not possible that a variable is, e.g., defined by two equations, which would give rise to conflicts or non-deterministic behavior. +Furthermore, the continuous and the discrete parts of a model are always automatically "synchronized". +Example: + +[source,modelica] +---- +equation // Illegal example + when condition1 then + close = true; + end when; + + when condition2 then + close = false; + end when; +---- + +This is not a valid model because rule 4 is violated since there are two equations for the single unknown variable close. +If this would be a valid model, a conflict occurs when both conditions become true at the same time instant, since no priorities between the two equations are assigned. +To become valid, the model has to be changed to: + +[source,modelica] +---- +equation + when condition1 then + close = true; + elsewhen condition2 then + close = false; + end when; +---- + +Here, it is well-defined if both conditions become true at the same time instant (`condition1` has a higher priority than `condition2`). +==== + +There is no guarantee that two different events occur at the same time instant. + +[example] +==== +As a consequence, synchronization of events has to be explicitly programmed in the model, e.g., via counters. +Example: + +[source,modelica] +---- + Boolean fastSample, slowSample; + Integer ticks(start=0); +equation + fastSample = sample(0,1); +algorithm + when fastSample then + ticks := if pre(ticks) < 5 then pre(ticks)+1 else 0; + slowSample := pre(ticks) == 0; + end when; +algorithm + when fastSample then // fast sampling + ... + end when; +algorithm + when slowSample then // slow sampling (5-times slower) + ... + end when; +---- + +The `slowSample` `when`-clause is evaluated at every 5th occurrence of the `fastSample` `when`-clause. +==== + +[NOTE] +The single assignment rule and the requirement to explicitly program the synchronization of events allow a certain degree of model verification already at compile time. + +=== Initialization, initial equation, and initial algorithm + +Before any operation is carried out with a Modelica model (e.g., simulation or linearization), initialization takes place to assign consistent values for all variables present in the model. +During this phase, called the _initialization problem_, also the derivatives (`der`), and the pre-variables (`pre`), are interpreted as unknown algebraic variables. +The initialization uses all equations and algorithms that are utilized in the intended operation (such as simulation or linearization). + +The equations of a `when`-clause are active during initialization, if and only if they are explicitly enabled with `initial()`, and only in one of the two forms `when initial() then` or `when {..., initial(), ...} then` (and similarly for `elsewhen` and algorithms see below). +In this case, the `when`-clause equations remain active during the whole initialization phase. +In case of a `reinit(x, expr)` being active during initialization (due to being inside `when initial()`) this is interpreted as adding `x = expr` (the `reinit`-equation) as an initial equation. +The `reinit` handling applies both if directly inside `when`-clause or inside an `if`-equation in the `when`-clause. +In particular, `reinit(x, expr)` needs to be counted as the equation `x = expr;` for the purpose of balancing of `if`-equations inside `when`-clauses that are active during initialization, see <>. + +[NOTE] +If a `when`-clause equation `v = expr;` is not active during the initialization phase, the equation `v = pre(v);` is added for initialization. +This follows from the mapping rule of `when`-clause equations. +If the condition of the `when`-clause contains `initial()`, but not in one of the specific forms, the `when`-clause is not active during initialization: `when not initial() then print("simulation started"); end when;` + +The algorithmic statements within a `when`-statement are active during initialization, if and only if they are explicitly enabled with `initial()`, and only in one of the two forms `when initial() then` or `when {..., initial(), ...} then`. +In this case, the algorithmic statements within the `when`-statement remain active during the whole initialization phase. + +An active `when`-clause inactivates the following `elsewhen` (similarly as for `when`-clauses during simulation), but apart from that the first `elsewhen initial() then` or `elsewhen {..., initial(), ...} then` is similarly active during initialization as `when initial() then` or `when {..., initial(), ...} then`. + +[NOTE] +That means that any subsequent `elsewhen initial()` has no effect, similarly as `when false then`. + +[NOTE] +There is no special handling of inactive `when`-statements during initialization, instead variables assigned in `when`-statements are initialized using `v := pre(v)` before the body of the algorithm (since they are discrete), see <>. + +Further constraints, necessary to determine the initial values of all variables (depending on the component variability, see <> for definitions), can be defined in the following ways: + +. As equations in an `initial equation` section or as assignments in an `initial algorithm` section. The equations and assignments in these initial sections are purely algebraic, stating constraints between the variables at the initial time instant. It is not allowed to use `when`-clauses in these sections. +. For a continuous-time `Real` variable `vc`, the equation `pre(vc) = vc` is added to the initialization equations. ++ +[NOTE] +If `pre(vc)` is not present in the flattened model, a tool may choose not to introduce this equation, or if it was introduced it can eliminate it (to avoid the introduction of many dummy variables `pre(vc)`). +. Implicitly by using the `start`-attribute for variables with `fixed = true`. + With `start` given by `startExpression`: + * For a variable declared as `constant` or `parameter`, no equation is added to the initialization equations. + * For a discrete-time variable `vd`, the equation `pre(vd) = startExpression` is added to the initialization equations. + * For a continuous-time `Real` variable `vc`, the equation `vc = startExpression` is added to the initialization equations. + +Constants shall be determined by declaration equations (see <>), and `fixed = false` is not allowed. +For parameters, `fixed` defaults to `true`. +For other variables, `fixed` defaults to `false`. + +`start`-values of variables having `fixed = false` can be used as initial guesses, in case iterative solvers are used in the initialization phase. + +[NOTE] +In case of iterative solver failure, it is recommended to specially report those variables for which the solver needs an initial guess, but where the fallback value (see <>) has been applied, since the lack of appropriate initial guesses is a likely cause of the solver failure. + +If a parameter has a value for the `start`-attribute, does not have `fixed = false`, and neither has a binding equation nor is part of a record having a binding equation, the value for the `start`-attribute can be used to add a parameter binding equation assigning the parameter to that `start` value. +In this case a diagnostic message is recommended in a simulation model, unless the parameter has a `Dialog.enable` annotation set to false. + +[NOTE] +This is used in libraries to give rudimentary defaults so that users can quickly combine models and simulate without setting parameters; but still easily find the parameters that should be set properly. +The `enable=false` case can be used to provide default values for parameters that are not used in the current configuration, while ensuring that they are explicitly given a value when used. + +All variables declared as `parameter` having `fixed = false` are treated as unknowns during the initialization phase, i.e., there must be additional equations for them -- and the `start`-value can be used as a guess-value during initialization. + +[NOTE] +-- +In the case a parameter has both a binding equation and `fixed = false` a diagnostic is recommended, but the parameter should be solved from the binding equation. + +Continuous-time `Real` variables `vc` have exactly one initialization value since the rules above assure that during initialization `vc = pre(vc) = vc.startExpression` (if `fixed = true`). + +Before the start of the integration, it must be guaranteed that for all variables `v`, `v = pre(v)`. +If this is not the case for some variables `vi`, `pre(vi) := vi` must be set and an event iteration at the initial time must follow, so the model is re-evaluated, until this condition is fulfilled. +In detail this means that during initialization initial equations and normal equations are solved with `v` and `pre(v)` as unknowns without any event iterations. +Then only the normal equations are solved repeatedly (each time after `v` is copied to `pre(v)`) until `v = pre(v)`. + +Tools may optimize initialization by not computing unnecessary `pre(v)`, and only performing the event iteration if necessary. + +A Modelica translator may first transform the continuous equations of a model, at least conceptually, to state space form. +This may require to differentiate equations for index reduction, i.e., additional equations and, in some cases, additional unknown variables are introduced. +This whole set of equations, together with the additional constraints defined above, should lead to an algebraic system of equations where the number of equations and the number of all variables (including `der` and `pre` variables) is equal. +Often, this is a nonlinear system of equations and therefore it may be necessary to provide appropriate guess values (i.e., `start` values and `fixed = false`) in order to compute a solution numerically. + +It may be difficult for a user to figure out how many initial equations have to be added, especially if the system has a higher index. +-- + +These non-normative considerations are addressed as follows. +A tool may add or remove initial equations automatically according to the rules below such that the resulting system is structurally nonsingular: + +* A missing initial value of a discrete-time variable (see <> -- this does not include parameter and constant variables) which does not influence the simulation result, may be automatically set to the start value or its default without informing the user. For example, variables assigned in a `when`-clause which are not accessed outside of the `when`-clause and where `pre` is not explicitly used on these variables, do not have an effect on the simulation. +* A `start`-attribute that is not fixed may be treated as fixed with a diagnostic. +* A consistent start value or initial equation may be removed with a diagnostic. + +[NOTE] +The goal is to be able to initialize the model, while satisfying the initial equations and fixed start values. + +[example] +==== +Example: Continuous time controller initialized in steady-state: + +[source,modelica] +---- + Real y(fixed = false); // fixed=false is redundant +equation + der(y) = a * y + b * u; +initial equation + der(y) = 0; +---- + +This has the following solution at initialization: +[source,modelica] +---- +der(y) = 0; +y = - b / a * u; +---- +==== + +[example] +==== +Example: Continuous time controller initialized either in steady-state or by providing a `start` value for state `y`: + +[source,modelica] +---- + parameter Boolean steadyState = true; + parameter Real y0 = 0 "start value for y, if not steadyState"; + Real y; +equation + der(y) = a * y + b * u; +initial equation + if steadyState then + der(y) = 0; + else + y = y0; + end if; +---- + +This can also be written as follows (this form is less clear): +[source,modelica] +---- + parameter Boolean steadyState = true; + Real y (start = 0, fixed = not steadyState); + Real der_y(start = 0, fixed = steadyState) = der(y); +equation + der(y) = a * y + b * u; +---- +==== + +[example] +==== +Example: Discrete-time controller initialized in steady-state: + +[source,modelica] +---- + discrete Real y; +equation + when {initial(), sampleTrigger} then + y = a * pre(y) + b * u; + end when; +initial equation + y = pre(y); +---- + +This leads to the following equations during initialization: +[source,modelica] +---- +y = a * pre(y) + b * u; +y = pre(y); +---- +with the solution: +[source,modelica] +---- +y := (b * u) / (1 - a); +pre(y) := y; +---- +==== + +[example] +==== +Example: Resettable continuous-time controller initialized either in steady-state or by providing a `start` value for state `y`: + +[source,modelica] +---- + parameter Boolean steadyState = true; + parameter Real y0 = 0 "start and reset value for y, if not steadyState"; + input Boolean reset "For resetting integrator to y0"; + Real y; +equation + der(y) = a * y + b * u; + when {initial(), reset} then + if not (initial() and steadyState) then + reinit(y, y0); + end if; + end when; +initial equation + if steadyState then + der(y) = 0; + end if; +---- + +If `not steadyState` this will add `y = y0` during the initialization; if not the `reinit` is ignored during initialization and the initial equation is used. +This model can be written in various ways, this particular way makes it clear that the reset is equal to the normal initialization. + +During initialization this gives the following equations + +[source,modelica] +---- + if not steadyState then + y = y0; + end if; + if steadyState then + der(y) = 0; + end if; +---- + +if `steadyState` had not been a parameter-expression both of those equations would have been illegal according to the restrictions in <>. +==== + +==== Equations Needed for Initialization + +[NOTE] +In general, for the case of a pure (first order) ordinary differential equation (ODE) system with latexmath:[n] state variables and latexmath:[m] output variables, we will have latexmath:[n+m] unknowns during transient analysis. +The ODE initialization problem has latexmath:[n] additional unknowns corresponding to the derivative variables. +During initialization of an ODE we will need to find the values of latexmath:[2n+m] variables, in contrast to just latexmath:[n+m] variables to be solved for during transient analysis. + +[example] +==== +Example: Consider the following simple equation system: + +[source,modelica] +---- +der(x1) = f1(x1); +der(x2) = f2(x2); +y = x1+x2+u; +---- + +Here we have three variables with unknown values: two dynamic variables that also are state variables, `x1` and `x2`, i.e., latexmath:[n=2], one output variable `y`, i.e., latexmath:[m=1], and one input variable `u` with known value. +A consistent solution of the initialization problem requires finding initial values for `x1`, `x2`, `der(x1)`, `der(x2)`, and `y`. +Two additional initial equations thus need to be provided to obtain a globally balanced initialization problem. +Additionally, those two initial equations must be chosen with care to ensure that they, in combination with the dynamic equations, give a well-determined initialization problem. + +Regarding DAEs, only that at most latexmath:[n] additional equations are needed to arrive at latexmath:[2n+m] equations in the initialization system. +The reason is that in a higher index DAE problem the number of dynamic continuous-time state variables might be less than the number of state variables latexmath:[n]. +As noted in <> a tool may add/remove initial equations to fulfill this requirement, if appropriate diagnostics are given. +==== + +==== Start Value Recommended Priority + +In general many variables have `start`-attributes that are not fixed and selecting a subset of these can give a consistent set of start values close to the user-expectations. +The following gives a non-normative procedure for finding such a subset. + +[NOTE] +A model has a hierarchical component structure. +Each component of a model can be given a unique model component hierarchy level number. +The top-level model has a level number of 1. +The level number increases by 1 for each level down in the model component hierarchy. +The model component hierarchy level number is used to give `start`-attribute a confidence number, where a lower number means that the `start`-attribute is more confident. +Loosely, if the `start`-attribute is set or modified on level latexmath:[i] then the confidence number is latexmath:[i]. +If a `start`-attribute is set by a possibly hierarchical modifier at the top level, then this `start`-attribute has the highest confidence, namely 1 irrespectively on what level, the variable itself is declared. +If the `start`-attribute is set equal to a parameter, which may be equal to another parameter (etc), the lowest confidence number of these bindings are used. +(In almost all cases that is the confidence number of the last parameter binding in the chain.) +Note that this is only applied if the expression is exactly the parameter -- not an expression depending on one or more parameters. +In case the confidence number considering parameter bindings is tied the confidence number of the `start`-attribute is used to break the tie, if unequal. + +[example] +==== +Example: Simplified examples showing the priority of start-values. +The example `M3` shows that it is important that parameter-confidence is used directly and not only when the other priority is tied. + +[source,modelica] +---- +model M1 + Real x(start = 4.0); + Real y(start = 5.0); +equation + x = y; +end M1; +model M2 + parameter Real xStart = 4.0; + parameter Real yStart = 5.0; + Real x(start = xStart); + Real y(start = yStart); +equation + x = y; +end M2; +model M3 + model MLocal + parameter Real xStart = 4.0; + Real x(start = xStart); + end MLocal; + model MLocalWrapped + parameter Real xStart = 4.0; + MLocal m(xStart = xStart); + end MLocalWrapped; + MLocal mx; + MLocalWrapped my(xStart = 3.0); +equation + mx.x = my.y; +end M3; +M1 m1(x(start = 3.0)); +// Using m1.x.start = 3.0 with confidence number 1 +// over m1.y.start = 5.0 with confidence number 2 +M2 m2(xStart = 3.0); +// Using m2.x.start = m2.xStart = 3.0 with confidence number 1 +// over m2.y.start = m2.yStart = 5.0 with confidence number 2 +M3 m3; +// Using m3.my.x = m3.my.xStart = 3.0 with confidence number 1 +// over m3.mx.x = m3.mx.xStart = 4.0 with confidence number 2 +---- +==== \ No newline at end of file diff --git a/docs/9___connectors.adoc b/docs/9___connectors.adoc new file mode 100644 index 000000000..c7073cdf6 --- /dev/null +++ b/docs/9___connectors.adoc @@ -0,0 +1,1170 @@ +== Connectors and Connections +:id: connectors-and-connections + +This chapter covers connectors, `connect`-equations, and connections. + +Connectors and `connect`-equations are designed so that different components can be connected graphically with well-defined semantics. +However, the graphical part is optional and found in <>. + +=== Connect-Equations and Connectors + +Connections between objects are introduced by `connect`-equations in the equation part of a class. +A `connect`-equation has the following syntax: + +[source,grammar] +---- +connect "(" component-reference "," component-reference ")" ";" +---- + +[NOTE] +A _connector_ is an instance of a `connector` class. + +The `connect`-equation construct takes two references to connectors, each of which is either of the following forms: + +* latexmath:[c_1.c_2.\ldots{}.c_n], where latexmath:[c_1] is a connector of the class, latexmath:[n \geq 1] and latexmath:[c_{i+1}] is a connector element of latexmath:[c_i] for latexmath:[i = 1,\, \ldots,\, (n - 1)]. +* `m.c`, where `m` is a non-connector element in the class and `c` is a connector element of `m`. + +There may optionally be array subscripts on any of the components; the array subscripts shall be evaluable expressions or the special operator `:`. +If the connect construct references arrays of connectors, the array dimensions must match, and each corresponding pair of elements from the arrays is connected as a pair of scalar connectors. + +[example] +==== +Example: Array usage: + +[source,modelica] +---- +connector InPort = input Real; +connector OutPort = output Real; +block MatrixGain + input InPort u[size(A, 2)]; + output OutPort y[size(A, 1)]; + parameter Real A[:, :] = [1]; +equation + y = A * u; +end MatrixGain; + +Modelica.Blocks.Sources.Sine sinSource[5]; +MatrixGain gain (A = 5 * identity(5)); +MatrixGain gain2(A = ones(2, 5)); +OutPort x[2]; +equation + connect(sinSource.y, gain.u); // Legal + connect(gain.y, gain2.u); // Legal + connect(gain2.y, x); // Legal +---- +==== + +The three main tasks are to: + +* Elaborate expandable connectors. +* Build connection sets from `connect`-equations. +* Generate equations for the complete model (connection equations). + +==== Connection Sets + +A connection set is a set of variables connected by means of `connect`-equations. +A connection set shall contain either only flow variables or only non-flow variables. + +==== Inside and Outside Connectors + +In an element instance `M`, each connector element of `M` is called an _outside connector_ with respect to `M`. +Any other connector element that is hierarchically inside `M`, but not in one of the outside connectors of `M`, is called an _inside connector_ with respect to `M`. +This is done before resolving `outer` elements to corresponding `inner` ones. + +[example] +==== +Example: + +image::media/innerouterconnector.svg[] + +The figure visualizes the following `connect`-equations to the `connector` `c` in the models `mi`. +Consider the following `connect`-equations found in the model for component `m0`: + +[source,modelica] +---- +// In model for component m0: +connect(m1.c, m3.c); // m1.c and m3.c are inside connectors +connect(m2.c, m3.c); // m2.c and m3.c are inside connectors +---- + +and in the model for component `m3` (`c.x` is a sub-connector inside `c`): + +[source,modelica] +---- +connect(c, m4.c); // c is an outside connector, m4.c is an inside connector +connect(c.x, m5.c); // c.x is an outside connector, m5.c is an inside connector +connect(c, d); // c is an outside connector, d is an outside connector +---- + +and in the model for component `m6`: + +[source,modelica] +---- +connect(d, m7.c); // d is an outside connector, m7.c is an inside connector +---- +==== + +==== Expandable Connectors + +If the `expandable` qualifier is present on a connector definition, all instances of that connector are referred to as _expandable connectors_. +Instances of connectors that do not possess this qualifier are _non-expandable connectors_. + +Before generating connection equations, non-parameter scalar variables and non-parameter array elements declared in expandable connectors are marked as only being potentially present. +A non-parameter array element may be declared with array dimensions `:` indicating that the size is unknown (note that the size of such a dimension cannot be determined using `size`, see <>). +This applies to both variables of simple types, and variables of structured types. + +Then connections containing expandable connectors are elaborated: + +. If a `connect`-equation references a potentially present component as part of the argument it will be marked as being present, which will allow a connection to an undeclared connector inside it. +The rule does not apply for the complete argument. + +. After that at least one connector in the `connect`-equation must reference a declared component. + +. If the other connector is undeclared it must be in a declared component and is handled as follows: + +* The expandable connector instance is automatically augmented with a new component having the used name and corresponding type. + +* If the undeclared component is subscripted, an array variable is created, and a connection to the specific array element is performed. +Introducing elements in an array gives an array with at least the specified elements, other elements are either not created or have a default value (i.e., as if they were only potentially present, and the same note regarding the use of size also applies here). + +* If the variable on the other side of the `connect`-equation is `input` or `output` the new component will be either `input` or `output` to satisfy the restrictions in <> for a non-expandable connector. ++ +[NOTE] +The general rule ensures consistency for inside and outside connectors, and handles multiple connections to the new component. +In the simple case of no other connections involving these variables and the existing side referring to an inside connector (i.e., a connector of a component), the new variable will copy its causality (i.e., `input` if `input` and `output` if `output`) since the expandable connector must be an outside connector. ++ +For an array the `input`/`output` property can be deduced separately for each array element. + +Additionally: + +* When two expandable connectors are connected, each is augmented with the variables that are only declared in the other expandable connector (the new variables are neither `input` nor `output`). This is repeated until all connected expandable connector instances have matching variables. ++ +[NOTE] +I.e., each of the connector instances is expanded to be the union of all connector variables. + +* The variables introduced in the elaboration follow additional rules for generating connection sets (given in <>). + +* If a variable appears as an `input` in one expandable connector, it should appear as a non-`input` in at least one other expandable connector instance in the same augmentation set. +An augmentation set is defined as the set of connected expandable connector instances that through the elaboration will have matching variables. ++ +[example] +==== +Example: + +[source,modelica] +---- +expandable connector EngineBus +end EngineBus; + +partial block Sensor + RealOutput speed; // Output, i.e., non-input +end Sensor; +partial block Actuator + RealInput speed; // Input +end Actuator; + +model SensorWBus + EngineBus bus; + replaceable Sensor sensor; +equation + connect(bus.speed, sensor.speed); // Provides 'speed' +end SensorWBus; +model ActuatorWBus + EngineBus bus; + replaceable Actuator actuator; +equation + connect(bus.speed, actuator.speed); // Uses 'speed' +end ActuatorWBus; + +model Engine + ActuatorWBus actuator; + SensorWBus sensor; + EngineBus bus; +equation + connect(bus, actuator.bus); + connect(bus, sensor.bus); +end Engine; +---- + +This small example shows how expandable connectors are normally used: + +* There are a number of bus-instances all connected together. +* There is one source of the signal: `sensor.sensor.speed`. +* There are zero or more uses of the signal: `actuator.actuator.speed`. +==== + +* All components in an expandable connector are seen as connector instances even if they are not declared as such. ++ +[NOTE] +I.e., it is possible to connect to, e.g., a `Real` variable. ++ +[example] +==== +Example: + +[source,modelica] +---- +expandable connector EngineBus // has predefined signals + import Modelica.Units.SI; + SI.AngularVelocity speed; + SI.Temperature T; +end EngineBus; + +partial block Sensor + RealOutput speed; +end Sensor; + +model Engine + EngineBus bus; + replaceable Sensor sensor; +equation + connect(bus.speed, sensor.speed); + // connection to non-connector speed is possible + // in expandable connectors +end Engine; +---- +==== + +* An expandable connector shall not contain a component declared with the prefix `flow`, but may contain non-expandable connector components with `flow` components. ++ +[example] +==== +Example: + +[source,modelica] +---- +import Interfaces=Modelica.Electrical.Analog.Interfaces; +expandable connector ElectricalBus + Interfaces.PositivePin p12, n12; // OK + flow Modelica.Units.SI.Current i; // Error +end ElectricalBus; + +model Battery + Interfaces.PositivePin p42, n42; + ElectricalBus bus; +equation + connect(p42, bus.p42); // Adds new electrical pin + connect(n42, bus.n42); // Adds another pin +end Battery; +---- +==== + +* Expandable connectors can only be connected to other expandable connectors. + +If a `connect`-equation references a potentially present variable, or variable element, in an expandable connector the variable or variable element is marked as being present, and due to the paragraphs above it is possible to deduce whether the bus variable shall be treated as input, or shall be treated as output in the `connect`-equation. +That `input` or `output` prefix is added if no `input`/`output` prefix is present on the declaration. + +[example] +==== +Example: + +[source,modelica] +---- +expandable connector EmptyBus +end EmptyBus; + +model Controller + EmptyBus bus1; + EmptyBus bus2; + RealInput speed; +equation + connect(speed, bus1.speed); // OK; only one undeclared and not subscripted. + connect(bus1.pressure, bus2.pressure); // Error; both undeclared. + connect(speed, bus2.speed[2]); // Introduces speed array (with element [2]). +end Controller; +---- +==== + +An expandable connector array component for which `size` is not defined (see <>) is referred to as a _sizeless array component_. +Such a component shall not be used without subscripts, and the subscripts must slice the array so that the sizeless dimensions are removed. + +[example] +==== +Example: Valid and invalid uses of sizeless array components: + +[source,modelica] +---- +expandable connector EngineBus +end EngineBus; + +partial block Sensor + RealOutput speed; +end Sensor; + +model Engine + parameter Integer n = 1; + EngineBus bus; + replaceable Sensor sensor; + RealOutput sensorSpeeds[:]; +equation + connect(bus.speed[n], sensor.speed) ; // OK; subscript to scalar component. + connect(bus.speed, sensorSpeeds); // Error; missing subscripts. +public + Real a[:] = bus.speed; // Error; missing subscripts. + Real b[2] = bus.speed[{1, 3}]; // OK; subscript selects fixed size sub-array. +end Engine; +---- +==== + +After this elaboration the expandable connectors are treated as normal connector instances, and the connections as normal connections, and all potentially present variables and array elements that are not actually present are undefined. +It is an error if there are expressions referring to potentially present variables or array elements that are not actually present or non-declared variables. +This elaboration implies that expandable connectors can be connected even if they do not contain the same components. + +[NOTE] +A tool may remove undefined variables in an expandable connector, or set them to the default value, e.g., zero for `Real` variables. + +[NOTE] +Expressions can only "read" variables from the bus that are actually declared and present in the connector, in order that the types of the variables can be determined in the local scope. + +[NOTE] +-- +Note that the introduction of variables, as described above, is conceptual and does not necessarily impact the flattening hierarchy in any way. Furthermore, it is important to note that these elaboration rules must consider: + +. Expandable connectors nested hierarchically. This means that both outside and inside connectors must be included at every level of the hierarchy in this elaboration process. + +. When processing an expandable connector that possesses the `inner` scope qualifier, all outer instances must also be taken into account during elaboration. +-- + +[example] +==== + +Example: Engine system with sensors, controllers, actuator and plant that exchange information via a bus (i.e., via expandable connectors): + +[source,modelica] +---- +import Modelica.Units.SI; +import Modelica.Blocks.Interfaces.RealInput; +// Plant Side +model SparkPlug + RealInput spark_advance; + ... +end SparkPlug; + +expandable connector EngineBus + // No minimal set +end EngineBus; + +expandable connector CylinderBus + Real spark_advance; +end CylinderBus; + +model Cylinder + CylinderBus cylinder_bus; + SparkPlug spark_plug; + ... +equation + connect(spark_plug.spark_advance, + cylinder_bus.spark_advance); +end Cylinder; + +model I4 + EngineBus engine_bus; + Modelica.Mechanics.Rotational.Sensors.SpeedSensor speed_sensor; + Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temp_sensor; + parameter Integer nCylinder = 4 "Number of cylinders"; + Cylinder cylinder[nCylinder]; +equation + // adds engine_speed (as output) + connect(speed_sensor.w, engine_bus.engine_speed); + // adds engine_temp (as output) + connect(temp_sensor.T, engine_bus.engine_temp); + // adds cylinder_bus1 (a nested bus) + for i in 1:nCylinder loop + connect(cylinder[i].cylinder_bus, + engine_bus.cylinder_bus[i]); + end for; +end I4; +---- + +Due to the above connection, conceptually a connector consisting of the union of all connectors is introduced. + +The `engine_bus` contains the following variable declarations: +[source,modelica] +---- +RealOutput engine_speed; +RealOutput engine_temp; +CylinderBus cylinder_bus[1]; +CylinderBus cylinder_bus[2]; +CylinderBus cylinder_bus[3]; +CylinderBus cylinder_bus[4]; +---- +==== + +=== Generation of Connection Equations + +When generating _connection equations_, `outer` elements are resolved to the corresponding `inner` elements in the instance hierarchy (see instance hierarchy name lookup <>). +The arguments to each `connect`-equation are resolved to two connector elements. + +For every use of the `connect`-equation +[source,modelica] +---- +connect(a, b); +---- +a connection set is generated for each pair of corresponding primitive components of `a` and `b` together with an indication of whether they are from an inside or an outside connector. + +[[primitive-elements]] +Defintion Primitive elements:: +The primitive elements are of simple types or of types defined as `operator record` (i.e., a component of an `operator record` type is not split into sub-components). + +The elements of the connection sets are tuples of primitive variables together with an indication of inside or outside; if the same tuple belongs to two connection sets those two sets are merged, until every tuple is only present in one set. +Composite connector types are broken down into primitive components. +The `outer` components are handled by mapping the objects to the corresponding `inner` components, and the inside indication is not influenced. +The outer connectors are handled by mapping the objects to the corresponding inner connectors, and they are always treated as outside connectors. + +[NOTE] +Rationale: The inside/outside as part of the connection sets ensure that connections from different hierarchical levels are treated separately. +Connection sets are formed from the primitive elements and not from the connectors; this handles connections to parts of hierarchical connectors and also makes it easier to generate equations directly from the connection sets. All variables in one connection set will either be flow variables or non-flow variables due to restriction on `connect`-equations. +The mapping from an `outer` to an `inner` element must occur before merging the sets in order to get one zero-sum equation, and ensures that the equations for the `outer` elements are all given for one side of the connector, and the `inner` element can define the other side. + +The following connection sets with just one member are also present (and merged): + +* Each primitive flow variable as inside connector. + +* Each flow variable added during augmentation of expandable connectors, both as inside and as outside. ++ +[NOTE] +Note that the flow variable is not directly in the expandable connector, but in a connector inside the expandable connector. + +[NOTE] +Rationale: If these variables are not connected they will generate a set comprised only of this element, and thus they will be implicitly set to zero (see below). +If connected, this set will be merged and adding this at the start has no impact. + +Each connection set is used to generate equations for potential and flow (zero-sum) variables of the form + +* latexmath:[a_{1} = a_{2} = \ldots = a_{n}] (neither flow nor stream variables) + +* latexmath:[z_{1} + z_{2} + (-z_{3}) + \ldots + z_{n} = \mathbf{0}] (flow variables) + +The bold-face latexmath:[\mathbf{0}] represents an array or scalar zero of appropriate dimensions (i.e., the same size as latexmath:[z]). + +For an `operator record` type this uses the operator `'0'` -- which must be defined in the operator record -- and all of the flow variables for the `operator record` must be of the same `operator record` type. +This implies that in order to have flow variables of an `operator record` type the `operator record` must define addition, negation, and `'0'`; and these operations should define an additive group. + +In order to generate equations for flow variables (using the `flow` prefix), the sign used for the connector variable latexmath:[z_{i}] above is +1 for inside connectors and -1 for outside connectors (latexmath:[z_{3}] in the example above). + +[example] +==== +Example: Simple example: + +[source,modelica] +---- +model Circuit + Ground ground; + Load load; + Resistor resistor; +equation + connect(load.p , ground.p); + connect(resistor.p, ground.p); +end Circuit; + +model Load + extends TwoPin; + Resistor resistor; +equation + connect(p, resistor.p); + connect(resistor.n, n); +end Load; +---- + +The connection sets are before merging (note that one part of the load and resistor is not connected): + +``` +{} +{} +{} +{} +{} +{} +{} +{, } +{, } +{, } +{, } +{, } +{, } +{, } +{, } +``` + +After merging this gives: + +``` +{, } +{, } +{, } +{, } +{, , } +{, , } +{} +{} +``` + +And thus the equations: +[source,modelica] +---- +load.p.v = load.resistor.p.v; +load.n.v = load.resistor.n.v; +load.p.v = ground.p.v; +load.p.v = resistor.p.v; +0 = (-load.p.i) + load.resistor.p.i; +0 = (-load.n.i) + load.resistor.n.i; +0 = load.p.i + ground.p.i + resistor.p.i; +0 = load.n.i; +0 = resistor.n.i; +---- +==== + +[example] +==== +Example: Outer component example: + +[source,modelica] +---- +model Circuit + Ground ground; + Load load; + inner Resistor resistor; +equation + connect(load.p, ground.p); +end Circuit; + +model Load + extends TwoPin; + outer Resistor resistor; +equation + connect(p, resistor.p); + connect(resistor.n, n); +end Load; +---- + +The connection sets are before merging: + +``` +{} +{} +{} +{} +{} +{, } +{, } +{, } +{, } +{, } +{, } +``` + +After merging this gives: + +``` +{, } +{, } +{, } +{, } +{, } +{, } +{} +``` + +And thus the equations: +[source,modelica] +---- +load.p.v = resistor.p.v; +load.n.v = resistor.n.v; +load.p.v = ground.p.v; +0 = (-load.p.i) + resistor.p.i; +0 = (-load.n.i) + resistor.n.i; +0 = load.p.i + ground.p.i; +0 = load.n.i; +---- + +This corresponds to a direct connection of the resistor. +==== + +=== Restrictions of Connections and Connectors + +* The `connect`-equations (and some special functions for overdetermined connectors) can only be used in normal equations and in some `if`-equations and `for`-equations. + See <> for details. ++ +[NOTE] +==== +The `for`-equations always have evaluable expressions for the array expression. +==== + +* A connector component shall not be declared with the prefix `parameter` or `constant`. + In the `connect`-equation the primitive components may only connect parameter variables to parameter variables and constant variables to constant variables. + +* The `connect`-equation construct only accepts forms of connector references as specified in <>. + +* In a `connect`-equation the two connectors must have the same named component elements with the same dimensions; recursively down to the primitive components. + The primitive components with the same name are matched and belong to the same connection set. + +* The matched primitive components of the two connectors must have the same primitive types, and flow variables may only connect to other flow variables, stream variables only to other stream variables, and causal variables (`input`/`output`) only to causal variables (`input`/`output`). + +* A connection set of causal variables (`input`/`output`) may at most contain variables from one inside `output` connector (for state-machines extended as specified in <>) or one public outside `input` connector. ++ +[NOTE] +==== +I.e., a connection set may at most contain one source of a signal. +==== + +* At least one of the following must hold for a connection set containing causal variables generated for a non-partial model or block: ++ +. the connection set includes variables from an outside public expandable connector, +. the set contains variables from protected outside connectors, +. it contains variables from one inside `output` connector, or +. from one public outside `input` connector, or +. the set is comprised solely of one variable from one inside `input` connector that is not part of an expandable connector. ++ +[NOTE] +==== +I.e., a connection set must -- unless the model or block is partial -- contain one source of a signal (item 5 covers the case where a connector of a component is left unconnected and the source given textually). +==== + +* Variables from a protected outside connector must be part of a connection set containing at least one inside connector or one declared public outside connector (i.e., it shall not be an implicitly defined part of an expandable connector). ++ +[NOTE] +==== +Otherwise it would not be possible to deduce the causality for the expandable connector element. +==== + +* [[connect-set-quantity-rule]]In a connection set, all variables having non-empty `quantity`-attribute must have the same `quantity`-attribute. ++ +[NOTE] +==== +Note that variables with different quantities may be unit-compatible, as seen in the example of work and torque. +It is also possible to include medium-information in the quantity to detect connections between different liquids. +==== + +* A `connect`-equation shall not (directly or indirectly) connect two connectors of `outer` elements. ++ +[NOTE] +==== +Indirectly is similar to them being part of the same connection set. +However, connections to `outer` elements are "moved up" before forming connection sets. +Otherwise the connection sets could contain redundant information breaking the equation count for locally balanced models and blocks. +==== + +* Subscripts in a connector reference shall be evaluable expressions or the special operator `:`. + +* Constants or parameters in connected components yield the appropriate `assert`-statements to check that they have the same value; connections are not generated. + +* For conditional connectors, see <>. + +==== Balancing Restriction and Size of Connectors + +For each non-partial non-simple non-expandable connector class the number of flow variables shall be equal to the number of variables that are neither `parameter`, `constant`, `input`, `output`, `stream` nor `flow`. +The _number of variables_ is the number of all elements in the connector class after expanding all records and arrays to a set of scalars of primitive types. +The number of variables of an overdetermined type or record class (see <>) is the size of the output argument of the corresponding `equalityConstraint`() function. +A simple connector class is not expandable, has some time-varying variables, and has neither `input`, `output`, `stream` nor `flow` variables. + +[NOTE] +==== +Expandable connector classes are excluded from this, since their component declarations are only a form of constraint. +==== + +A component of a simple connector class must be declared as `input`, `output`, or `protected`. + +[NOTE] +==== +A simple connector class is thus always unbalanced, but since it is used with causality or not visible to the outside it does not unbalance the model. +==== + +[example] +==== +Example: + +[source,modelica] +---- +connector Pin // A physical connector of Modelica.Electrical.Analog + Real v; + flow Real i; +end Pin; + +connector Plug // A hierarchical connector of Modelica.Electrical.MultiPhase + parameter Integer m = 3; + Pin p[m]; +end Plug; + +connector InputReal = input Real; // A causal input connector +connector OutputReal = output Real; // A causal output connector + +connector Frame_Illegal + Modelica.Units.SI.Position r0[3] "Position vector of frame origin"; + Real S[3, 3] "Rotation matrix of frame"; + Modelica.Units.SI.Velocity v[3] "Abs. velocity of frame origin"; + Modelica.Units.SI.AngularVelocity w[3] "Abs. angular velocity of frame"; + Modelica.Units.SI.Acceleration a[3] "Abs. acc. of frame origin"; + Modelica.Units.SI.AngularAcceleration z[3] "Abs. angular acc. of frame"; + flow Modelica.Units.SI.Force f[3] "Cut force"; + flow Modelica.Units.SI.Torque t[3] "Cut torque"; +end Frame_Illegal; +---- + +The `Frame_Illegal` connector (intended to be used in a simple multi-body package without over-determined connectors) is illegal since the number of flow and non-flow variables do not match. +The solution is to create two connector classes, where two 3-vectors (e.g., `a` and `z`) are acausal `Real` and the other variables are matching pairs of `input` and `output`. +This ensures that the models can only be connected in a tree-structure or require a "loop-breaker" joint for every closed kinematic loop: + +[source,modelica] +---- +connector Frame_a "correct connector" + input Modelica.Units.SI.Position r0[3]; + input Real S[3, 3]; + input Modelica.Units.SI.Velocity v[3]; + input Modelica.Units.SI.AngularVelocity w[3]; + Modelica.Units.SI.Acceleration a[3]; + Modelica.Units.SI.AngularAcceleration z[3]; + flow Modelica.Units.SI.Force f[3]; + flow Modelica.Units.SI.Torque t[3]; +end Frame_a; + +connector Frame_b "correct connector" + output Modelica.Units.SI.Position r0[3]; + output Real S[3, 3]; + output Modelica.Units.SI.Velocity v[3]; + output Modelica.Units.SI.AngularVelocity w[3]; + Modelica.Units.SI.Acceleration a[3]; + Modelica.Units.SI.AngularAcceleration z[3]; + flow Modelica.Units.SI.Force f[3]; + flow Modelica.Units.SI.Torque t[3]; +end Frame_b; +---- + +The subsequent connectors `Plug_Expanded` and `PlugExpanded2` are correct, but `Plug_Expanded_Illegal` is illegal since the number of non-flow and flow variables is different if `n` and `m` are different. +It is not clear how a tool can detect in general that connectors such as `Plug_Expanded_Illegal` are illegal. +However, it is always possible to detect this defect after actual values of parameters and constants are provided in the simulation model. + +[source,modelica] +---- +connector Plug_Expanded "correct connector" + parameter Integer m=3; + Real v[m]; + flow Real i[m]; +end Plug_Expanded; + +connector Plug_Expanded2 "correct connector" + parameter Integer m=3; + final parameter Integer n=m; + Real v[m]; + flow Real i[n]; +end Plug_Expanded2; + +connector Plug_Expanded_Illegal "connector is illegal" + parameter Integer m=3; + parameter Integer n=m; + Real v[m]; + flow Real i[n]; +end Plug_Expanded_Illegal; +---- +==== + +=== Overconstrained Connections + +There is a special problem regarding equation systems resulting from _loops_ in connection graphs where the connectors contain _non-flow_ (i.e., potential) variables _dependent_ on each other. +When a loop structure occurs in such a graph, the resulting equation system will be _overconstrained_, i.e., have more equations than variables, since there are implicit constraints between certain non-flow variables in the connector in addition to the connection equations around the loop. +At the current state-of-the-art, it is not possible to automatically eliminate the unneeded equations from the resulting equation system without additional information from the model designer. + +This section describes a set of equation operators for such overconstrained connection-based equation systems, that makes it possible for the model designer to specify enough information in the model to allow a Modelica environment to automatically remove the superfluous equations. + +[NOTE] +-- +Connectors may contain redundant variables. +For example, the orientation between two coordinate systems in latexmath:[3] dimensions can be described by latexmath:[3] independent variables. +However, every description of orientation with latexmath:[3] variables has at least one singularity in the region where the variables are defined. +It is therefore not possible to declare only latexmath:[3] variables in a connector. +Instead latexmath:[n] variables (latexmath:[n > 3]) have to be used. +These variables are no longer independent from each other and there are latexmath:[n - 3] constraint equations that have to be fulfilled. +A proper description of a redundant set of variables with constraint equations does no longer have a singularity. +A model that has loops in the connection structure formed by components and connectors with redundant variables, may lead to a differential algebraic equation system that has more equations than unknown variables. +The superfluous equations are usually consistent with the rest of the equations, i.e., a unique mathematical solution exists. +Such models cannot be treated with the currently known symbolic transformation methods. +To overcome this situation, operators are defined in order that a Modelica translator can remove the superfluous equations. +This is performed by replacing the equality equations of non-flow variables from connection sets by a reduced number of equations in certain situations. + +This section handles a certain class of overdetermined systems due to connectors that have a redundant set of variables. +There are other causes of overdetermined systems, e.g., explicit zero-sum equations for flow variables, that are not handled by the method described below. +-- + +==== Connection Graphs and Their Operators + +A type or record declaration may have an optional definition of function `equalityConstraint` that shall have the following prototype: + +[source,modelica] +---- +type Type // overdetermined type + extends ; + function equalityConstraint // non-redundant equality + input Type T1; + input Type T2; + output Real residue[n]; + algorithm + residue := ...; + end equalityConstraint; +end Type; + +record Record + ; + function equalityConstraint // non-redundant equality + input Record R1; + input Record R2; + output Real residue[n]; + algorithm + residue := ...; + end equalityConstraint; +end Record; +---- + +The array dimension latexmath:[n] of `residue` shall be a constant `Integer` expression that can be evaluated during translation, with latexmath:[n \ge 0]. +The `equalityConstraint` expresses the equality between the two type instances `T1` and `T2` or the record instances `R1` and `R2`, respectively, as the latexmath:[n] non-redundant equation residuals returned in `residue`. +That is, the set of latexmath:[n] non-redundant equations stating that `R1 = R2` is given by the equation (`0` represents a vector of zeros of appropriate size): + +[source,modelica] +---- + Record R1, R2; +equation + 0 = Record.equalityConstraint(R1, R2); +---- + +[NOTE] +If the elements of a record `Record` are not independent from each other, the equation `R1 = R2` contains redundant equations. + +A type class with an `equalityConstraint` function declaration is called _overdetermined type_. +A record class with an `equalityConstraint` function definition is called _overdetermined record_. +A connector that contains instances of overdetermined type and/or record classes is called _overdetermined connector_. +An overdetermined type or record may neither have flow components nor may be used as a type of flow components. +If an array is used as argument to any of the `Connections.*` functions it is treated as one unit -- unlike `connect`, there is no special treatment of this case, compare <>. + +Every instance of an overdetermined type or record in an overdetermined connector is a node in a virtual connection graph that is used to determine when the standard equation `R1 = R2` or when the equation `0 = equalityConstraint(R1, R2)` has to be used for the generation of `connect`-equations. +The edges of the virtual connection graph are implicitly defined by `connect` and explicitly by `Connections.branch`, see table below. +`Connections` is a built-in package in global scope containing built-in operators. +Additionally, corresponding nodes of the virtual connection graph have to be defined as roots or as potential roots with functions `Connections.root` and `Connections.potentialRoot`, respectively. + +The overconstrained equation operators for connection graphs are listed below. +Here, `a` and `b` are connector instances that may be hierarchically structured, e.g., `a` may be an abbreviation for `enginePort.frame_a`. + +[cols="1,1,1"] +|=== +|Expression |Description |Details + +|`connect(a, b)` +|Optional spanning-tree edge +|<> + +|`Connections.branch(a.R, b.R)` +|Required spanning-tree edge +|<> + +|`Connections.root(a.R)` +|Definite root node +|<> + +|`Connections.potentialRoot(a.R, ...)` +|Potential root node +|<> + +|`Connections.isRoot(a.R)` +|Predicate for being selected as root +|<> + +|`Connections.rooted(a.R)` +|Predicate for being closer to root +|<> +|=== + +[[operator:connect,Operator connect]] +Operator connect:: ++ +[source,modelica] +---- +connect(a, b) +---- ++ +Except for redundant connections it defines an _optional spanning-tree edge_ from the overdetermined type or record instances in connector `a` to the corresponding overdetermined type or record instances in connector `b` for a virtual connection graph. +E.g., from `a.R` to `b.R`. +The full explanation will be given in <>. +The types of the corresponding overdetermined type or record instances shall be the same. + + +[[operator:Connections.branch,Operator Connections.branch]] +Operator Connections.branch:: ++ +[source,modelica] +---- +Connections.branch(a.R, b.R) +---- ++ +Defines a _required spanning-tree edge_ from the overdetermined type or record instance `R` in connector instance `a` to the corresponding overdetermined type or record instance `R` in connector instance `b` for a virtual connection graph. +This function can be used at all places where a `connect`-equation is allowed. ++ +[NOTE] +E.g., it is not allowed to use this function in a `when`-clause. +This definition shall be used if in a model with connectors `a` and `b` the overdetermined records `a.R` and `b.R` are algebraically coupled in the model, e.g., due to `b.R = f(a.R, )`. + +[[operator:Connections.root,Operator Connections.root]] +Operator Connections.root:: ++ +[source,modelica] +---- +Connections.root(a.R) +---- ++ +The overdetermined type or record instance `R` in connector instance `a` is a _(definite) root node_ in a virtual connection graph. ++ +[NOTE] +This definition shall be used if in a model with connector `a` the overdetermined record `a.R` is (consistently) assigned, e.g., from a parameter expressions. + +[[operator:Connections.potentialRoot,Operator Connections.potentialRoot]] +Operator Connections.potentialRoot:: ++ +[source,modelica] +---- +Connections.potentialRoot(a.R) +Connections.potentialRoot(a.R, priority=p) +---- ++ +The overdetermined type or record instance `R` in connector instance `a` is a _potential root node_ in a virtual connection graph with priority `p` (_p_ ≥ 0). +If no second argument is provided, the priority is zero. +`p` shall be an evaluable expression of type `Integer`. +In a virtual connection subgraph without a `Connections.root` definition, one of the potential roots with the lowest priority number is selected as root. ++ +[NOTE] +This definition may be used if in a model with connector `a` the overdetermined record `a.R` appears differentiated -- `der(a.R)` -- together with the _constraint equations_ of `a.R`, i.e., a non-redundant subset of `a.R` maybe used as states. + +[[operator:Connections.isRoot,Operator Connections.isRoot]] +Operator Connections.isRoot:: ++ +[source,modelica] +---- +Connections.isRoot(a.R) +---- ++ +Returns true, if the overdetermined type or record instance `R` in connector instance `a` is selected as a root in the virtual connection graph. + +[[operator:Connections.rooted,Operator Connections.rooted]] +Operator Connections.rooted:: ++ +[source,modelica] +---- +Connections.rooted(a.R) +rooted(a.R) // deprecated! +---- ++ +If the operator `Connections.rooted(a.R)` is used, or the equivalent but deprecated operator `rooted(a.R)`, then there must be exactly one `Connections.branch(a.R, b.R)` involving `a.R` (the argument of `Connections.rooted` must be the first argument of `Connections.branch`). +In that case `Connections.rooted(a.R)` returns true, if `a.R` is closer to the root of the spanning tree than `b.R`; otherwise false is returned. ++ +[NOTE] +This operator can be used to avoid equation systems by providing analytic inverses, see `Modelica.Mechanics.MultiBody.Parts.FixedRotation`. + +[NOTE] +Note, that `Connections.branch`, `Connections.root`, `Connections.potentialRoot` do not generate equations. +They only generate nodes and edges in the virtual graph for analysis purposes. + +==== Generation of Connection Graph Equations + +When generating connection graph equations, only the overdetermined components of a connector are considered. +The connection graph equations replace the equality-equations for variables that are neither flow nor stream in <>. + +===== Handle Connect-Equation Redundancy + +In order to eliminate any redundant `connect`-equation the following preparation is needed. + +[NOTE] +==== +In the common case where there is no `connect`-equation redundancy, a consequence of this preparation is that a `connect`-equation between connectors with _one_ overdetermined component may be directly turned into _one_ optional spanning-tree edge. +==== + +. The connection sets are built similarly to the normal way, but keeping the overdetermined components as primitives. +. Instead of generating the equality-equation for an overdetermined component, an optional spanning-tree edge in the virtual connection graph is constructed. + +[NOTE] +==== +If a connection set contains _n_ overdetermined components, and was built from _m_ `connect`-equations, then the connection set has a `connect`-equation redundancy of _m_ - (_n_ - 1) ≥ 0. +If there is no `connect`-equation redundancy (i.e., if _m_=_n_-1), the optional spanning-tree edges can be chosen to correspond to the `connect`-equations for overdetermined connectors. +If there is a non-zero `connect`-equation redundancy, there will always exist `connect`-equations without a corresponding optional spanning-tree edge. + +It is called _redundancy_ since this number of `connect`-equations could be removed without changing the connection set or the generated equations. +Situations with non-zero `connect`-equation redundancy include connectors connected directly to themselves, duplicated connections, and having all three pair-wise connections between the connectors `a`, `b` and `c`. +The latter case can be used to consistently handle conditional components (so that disabling `b` does not break the connection between `a` and `c`). +==== + +The selected optional spanning tree edges, together with all required spanning tree edges generated from `Connections.branch`, and nodes corresponding to definite and potential roots form the virtual connection graph. + +===== Spanning Trees + +Before connection equations are generated, the virtual connection graph is transformed into a set of spanning trees by removing optional spanning tree edges from the graph. +This is performed in the following way: + +. Root nodes are selected as follows: +.. Every definite root node defined via the `Connections.root`-equation is a root of one spanning tree. + It is an error if two (or more) definite root nodes are connected through required spanning tree edges. +.. The virtual connection graph may consist of sets of subgraphs that are not connected together. + Every subgraph in this set shall have at least one definite root node or one potential root node in a simulation model. + If a graph of this set does not contain any definite root node, then one potential root node in this subgraph that has the lowest priority number is selected to be the root of that subgraph. + The selection can be inquired in a class with function `Connections.isRoot`, see table above. + +. If there is a cycle among required spanning-tree-edges it is an error, as it is not possible to construct a spanning tree. + +. For a subgraph with _n_ selected roots, optional spanning-tree edges are removed such that the result is a set of _n_ spanning trees with the selected root nodes as roots. + +===== Equations + +After this analysis, the _connection graph equations_ are generated in the following way: + +. For every remaining optional spanning-tree edge in any of the spanning trees, the connection equations are generated according to <>. + For `connect(a, b)` with overdetermined connector `R`, this corresponds to the optional spanning-tree edge between `a.R` and `b.R` generating the equation `a.R = b.R`. + +. For every remaining optional spanning-tree edge not in any of the spanning trees, the connection equations are generated according to <>, except for overdetermined type or record instances `R`. + Here the equations `0 = R.equalityConstraint(a.R, b.R)` are generated instead of `a.R = b.R`. + +==== Examples + +.Example of a virtual connection graph. +[example] +==== +Example: + +image::media/overdetermined.svg[width=90%] +==== + +===== A Power Systems Overdetermined Connector + +[NOTE] +==== +An overdetermined connector for power systems based on the transformation theory of Park may be defined as: + +[source,modelica] +---- +type AC_Angle "Angle of source, e.g., rotor of generator" + extends Modelica.Units.SI.Angle; // AC_Angle is a Real number + // with unit = "rad" + function equalityConstraint + input AC_Angle theta1; + input AC_Angle theta2; + output Real residue[0] "No constraints"; + algorithm + /* make sure that theta1 and theta2 from joining edges are identical */ + assert(abs(theta1 - theta2) < 1.e-10, "Consistent angles"); + end equalityConstraint; +end AC_Angle; + +connector AC_Plug "3-phase alternating current connector" + import Modelica.Units.SI; + AC_Angle theta; + SI.Voltage v[3] "Voltages resolved in AC_Angle frame"; + flow SI.Current i[3] "Currents resolved in AC_Angle frame"; +end AC_Plug; +---- + +The currents and voltages in the connector are defined relatively to the harmonic, high-frequency signal of a power source that is essentially described by angle theta of the rotor of the source. +This allows much faster simulations, since the basic high frequency signal of the power source is not part of the differential equations. +For example, when the source and the rest of the line operates with constant frequency (= nominal case), then `AC_Plug.v` and `AC_Plug.i` are constant. +In this case a variable step integrator can select large time steps. +An element, such as a 3-phase inductor, may be implemented as: + +[source,modelica] +---- +model AC_Inductor + parameter Real X[3,3], Y[3,3]; // component constants + AC_Plug p; + AC_Plug n; + Real omega; +equation + Connections.branch(p.theta,n.theta); //edge in virtual graph + // since n.theta = p.theta + n.theta = p.theta; // pass angle theta between plugs + omega = der(p.theta); // frequency of source + zeros(3) = p.i + n.i; + X*der(p.i) + omega*Y*p.i = p.v - n.v; +end AC_Inductor +---- + +At the place where the source frequency, i.e., essentially variable theta, is defined, a `Connections.root` must be present: + +[source,modelica] +---- + AC_Plug p; +equation + Connections.root(p.theta); + p.theta = 2*Modelica.Constants.pi*50*time; // 50 Hz +---- + +The graph analysis performed with the virtual connection graph identifies the connectors, where the `AC_Angle` needs not to be passed between components, in order to avoid redundant equations. + +Note that the different sources do not integrate the frequency, as that increases the risk of numerical errors. +==== + +===== A 3-Dimensional Mechanical Systems Overdetermined Connector + +[NOTE] +==== +An overdetermined connector for 3-dimensional mechanical systems may be defined as: + +[source,modelica] +---- +type TransformationMatrix = Real[3,3]; +type Orientation "Orientation from frame 1 to frame 2" + extends TransformationMatrix; + function equalityConstraint + input Orientation R1 "Rotation from inertial frame to frame 1"; + input Orientation R2 "Rotation from inertial frame to frame 2"; + output Real residue[3]; + protected + Orientation R_rel "Relative Rotation from frame 1 to frame 2"; + algorithm + R_rel := R2*transpose(R1); + /* + If frame_1 and frame_2 are identical, R_rel must be + the unit matrix. If they are close together, R_rel can be + linearized yielding: + R_rel = [ 1, phi3, -phi2; + -phi3, 1, phi1; + phi2, -phi1, 1 ]; + where phi1, phi2, phi3 are the small rotation angles around + axis x, y, z of frame 1 to rotate frame 1 into frame 2. + The atan2 is used to handle large rotation angles, but does not + modify the result for small angles. + */ + residue := { Modelica.Math.atan2(R_rel[2, 3], R_rel[1, 1]), + Modelica.Math.atan2(R_rel[3, 1], R_rel[2, 2]), + Modelica.Math.atan2(R_rel[1, 2], R_rel[3, 3])}; + end equalityConstraint; +end Orientation; + +connector Frame "3-dimensional mechanical connector" + import Modelica.Units.SI; + SI.Position r[3] "Vector from inertial frame to Frame"; + Orientation R "Orientation from inertial frame to Frame"; + flow SI.Force f[3] "Cut-force resolved in Frame"; + flow SI.Torque t[3] "Cut-torque resolved in Frame"; +end Frame; +---- + +A fixed translation from a frame `a` to a frame `b` may be defined as: + +[source,modelica] +---- +model FixedTranslation + parameter Modelica.Units.SI.Position r[3]; + Frame frame_a, frame_b; +equation + Connections.branch(frame_a.R, frame_b.R); + frame_b.r = frame_a.r + transpose(frame_a.R)*r; + frame_b.R = frame_a.R; + zeros(3) = frame_a.f + frame_b.f; + zeros(3) = frame_a.t + frame_b.t + cross(r, frame_b.f); +end FixedTranslation; +---- + +Since the transformation matrix `frame_a.R` is algebraically coupled with `frame_b.R`, an edge in the virtual connection graph has to be defined. +At the inertial system, the orientation is consistently initialized and therefore the orientation in the inertial system connector has to be defined as root: + +[source,modelica] +---- +model InertialSystem + Frame frame_b; +equation + Connections.root(frame_b.R); + frame_b.r = zeros(3); + frame_b.R = identity(3); +end InertialSystem; +---- +==== \ No newline at end of file diff --git a/docs/A___syntax.adoc b/docs/A___syntax.adoc new file mode 100644 index 000000000..4407603ab --- /dev/null +++ b/docs/A___syntax.adoc @@ -0,0 +1,471 @@ +[appendix] +== Modelica Concrete Syntax +:id: modelica-concrete-syntax + +=== Lexical conventions + +The following syntactic metasymbols are used (extended BNF): + +[cols="2*a",options=autowidth] +|=== +|Syntax |Description + +|`[ ... ]` +|Optional + +|`{ ... }` +|Repeat zero or more times + +|`... \| ...` +|Alternatives + +|`"text"` +|The `text` is treated as a single token (no white-space between any characters) +|=== + +The following lexical units are defined: + +[source,grammar] +---- +IDENT = NON-DIGIT { DIGIT | NON-DIGIT } | Q-IDENT +Q-IDENT = "'" { Q-CHAR | S-ESCAPE } "'" +NON-DIGIT = "_" | letters "a" ... "z" | letters "A" ... "Z" +DIGIT = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" +Q-CHAR = NON-DIGIT | DIGIT | "!" | "#" | "$" | "%" | "&" | "(" | ")" + | "*" | "+" | "," | "-" | "." | "/" | ":" | ";" | "<" | ">" | "=" + | "?" | "@" | "[" | "]" | "^" | "{" | "}" | "|" | "~" | " " | """ +S-ESCAPE = "\'" | "\"" | "\?" | "\\" + | "\a" | "\b" | "\f" | "\n" | "\r" | "\t" | "\v" +STRING = "\"" { S-CHAR | S-ESCAPE } "\"" +S-CHAR = see below +UNSIGNED-INTEGER = DIGIT { DIGIT } +UNSIGNED-REAL = + UNSIGNED-INTEGER "." [ UNSIGNED-INTEGER ] + | UNSIGNED_INTEGER [ "." [ UNSIGNED_INTEGER ] ] + ( "e" | "E" ) [ "+" | "-" ] UNSIGNED-INTEGER + | "." UNSIGNED-INTEGER [ ( "e" | "E" ) [ "+" | "-" ] UNSIGNED-INTEGER ] +---- + +`S-CHAR` is any member of the Unicode character set (https://unicode.org; see <> for storing as UTF-8 on files) except double-quote `"`, and backslash `\`. + +For identifiers the redundant escapes (`\?` and `\"`) are the same as the corresponding non-escaped variants (`?` and `"`). The single quotes are part of an identifier. For example, the identifiers `'x'` and `x` are different. + +Note: + +* White-space and comments can be used between separate lexical units and/or symbols, and also separates them. Each lexical unit will consume the maximum number of characters from the input stream. White-space and comments cannot be used inside other lexical units, except for `STRING` and `Q-IDENT` where they are treated as part of the lexical unit. +* Concatenation of string literals requires a binary expression. For example, `"a" + "b"` evaluates to `"ab"`. There is no support for the C/C++ style of concatenating adjacent string literal tokens (for example, `"a" "b"` becoming `"ab"`). +* Modelica uses the same comment syntax as C++ and Java (i.e., `//` signals the start of a line comment and `/* ... */` is a multi-line comment); comments may contain any Unicode character. Modelica also has structured comments in the form of annotations and string comments. +* In the grammar, keywords of the Modelica language are highlighted with color, for example, `equation`. +* Productions use hyphen as separator both in the grammar and in the text. (Previously the grammar used underscore.) + +If a description-string starts with the tag `` or ``, the entire string is HTML encoded (assumed to end with `` or ``, but to be rendered as HTML even if the end-tag is missing). Otherwise, the entire string is rendered as is. HTML encoded content may contain links in the same way as class documentation, see <>. + +=== Grammar + +==== Stored Definition -- Within + +[source,grammar] +---- +stored-definition : + [ within [ name ] ";" ] + { [ final ] class-definition ";" } +---- + +==== Class Definition + +[source,grammar] +---- +class-definition : + [ encapsulated ] class-prefixes class-specifier + +class-prefixes : + [ partial ] + ( class + | model + | [ operator ] record + | block + | [ expandable ] connector + | type + | package + | [ pure | impure ] [ operator ] function + | operator + ) + +class-specifier : + long-class-specifier | short-class-specifier | der-class-specifier + +long-class-specifier : + IDENT description-string composition end IDENT + | extends IDENT [ class-modification ] description-string composition + end IDENT + +short-class-specifier : + IDENT "=" base-prefix type-specifier [ array-subscripts ] + [ class-modification ] description + | IDENT "=" enumeration "(" ( [ enum-list ] | ":" ) ")" description + +der-class-specifier : + IDENT "=" der "(" type-specifier "," IDENT { "," IDENT } ")" description + +base-prefix : + [ input | output ] + +enum-list : + enumeration-literal { "," enumeration-literal } + +enumeration-literal : + IDENT description + +composition : + element-list + { public element-list + | protected element-list + | equation-section + | algorithm-section + } + [ external [ language-specification ] + [ external-function-call ] [ annotation-clause ] ";" + ] + [ annotation-clause ";" ] + +language-specification : + STRING + +external-function-call : + [ component-reference "=" ] + IDENT "(" [ expression-list ] ")" + +element-list : + { element ";" } + +element : + import-clause + | extends-clause + | [ redeclare ] + [ final ] + [ inner ] [ outer ] + ( class-definition + | component-clause + | replaceable ( class-definition | component-clause ) + [ constraining-clause description ] + ) + +import-clause : + import + ( IDENT "=" name + | name [ ".*" | "." ( "*" | "{" import-list "}" ) ] + ) + description + +import-list : + IDENT { "," IDENT } +---- + +==== Extends + +[source,grammar] +---- +extends-clause : + extends type-specifier [ class-or-inheritance-modification ] [ annotation-clause ] + +constraining-clause : + constrainedby type-specifier [ class-modification ] + +class-or-inheritance-modification : + "(" [ argument-or-inheritance-modification-list ] ")" + +argument-or-inheritance-modification-list : + ( argument | inheritance-modification ) { "," ( argument | inheritance-modification ) } + +inheritance-modification : + break ( connect-equation | IDENT ) +---- + +==== Component Clause + +[source,grammar] +---- +component-clause : + type-prefix type-specifier [ array-subscripts ] component-list + +type-prefix : + [ flow | stream ] + [ discrete | parameter | constant ] + [ input | output ] + +component-list : + component-declaration { "," component-declaration } + +component-declaration : + declaration [ condition-attribute ] description + +condition-attribute : + if expression + +declaration : + IDENT [ array-subscripts ] [ modification ] +---- + +==== Modification + +[source,grammar] +---- +modification : + class-modification [ "=" modification-expression ] + | "=" modification-expression + +modification-expression : + expression + | break + +class-modification : + "(" [ argument-list ] ")" + +argument-list : + argument { "," argument } + +argument : + element-modification-or-replaceable + | element-redeclaration + +element-modification-or-replaceable : + [ each ] [ final ] ( element-modification | element-replaceable ) + +element-modification : + name [ modification ] description-string + +element-redeclaration : + redeclare [ each ] [ final ] + ( short-class-definition | component-clause1 | element-replaceable ) + +element-replaceable : + replaceable ( short-class-definition | component-clause1 ) + [ constraining-clause ] + +component-clause1 : + type-prefix type-specifier component-declaration1 + +component-declaration1 : + declaration description + +short-class-definition : + class-prefixes short-class-specifier +---- + +==== Equations + +[source,grammar] +---- +equation-section : + [ initial ] equation { some-equation ";" } + +algorithm-section : + [ initial ] algorithm { statement ";" } + +some-equation : + ( simple-expression "=" expression + | if-equation + | for-equation + | connect-equation + | when-equation + | component-reference function-call-args + ) + description + +statement : + ( component-reference ( ":=" expression | function-call-args ) + | "(" output-expression-list ")" ":=" + component-reference function-call-args + | break + | return + | if-statement + | for-statement + | while-statement + | when-statement + ) + description + +if-equation : + if expression then + { some-equation ";" } + { elseif expression then + { some-equation ";" } + } + [ else + { some-equation ";" } + ] + end if + +if-statement : + if expression then + { statement ";" } + { elseif expression then + { statement ";" } + } + [ else + { statement ";" } + ] + end if + +for-equation : + for for-indices loop + { some-equation ";" } + end for + +for-statement : + for for-indices loop + { statement ";" } + end for + +for-indices : + for-index { "," for-index } + +for-index : + IDENT [ in expression ] + +while-statement : + while expression loop + { statement ";" } + end while + +when-equation : + when expression then + { some-equation ";" } + { elsewhen expression then + { some-equation ";" } + } + end when + +when-statement : + when expression then + { statement ";" } + { elsewhen expression then + { statement ";" } + } + end when + +connect-equation : + connect "(" component-reference "," component-reference ")" +---- + +[[syntax-expressions]] +==== Expressions + +[source,grammar] +---- +expression : + simple-expression + | if expression then expression + { elseif expression then expression } + else expression + +simple-expression : + logical-expression [ ":" logical-expression [ ":" logical-expression ] ] + +logical-expression : + logical-term { or logical-term } + +logical-term : + logical-factor { and logical-factor } + +logical-factor : + [ not ] relation + +relation : + arithmetic-expression [ relational-operator arithmetic-expression ] + +relational-operator : + "<" | "<=" | ">" | ">=" | "==" | "<>" + +arithmetic-expression : + [ add-operator ] term { add-operator term } + +add-operator : + "+" | "-" | ".+" | ".-" + +term : + factor { mul-operator factor } + +mul-operator : + "*" | "/" | ".*" | "./" + +factor : + primary [ ( "^" | ".^" ) primary ] + +primary : + UNSIGNED-NUMBER + | STRING + | false + | true + | ( component-reference | der | initial | pure ) function-call-args + | component-reference + | "(" output-expression-list ")" [ ( array-subscripts | "." IDENT ) ] + | "[" expression-list { ";" expression-list } "]" + | "{" array-arguments "}" + | end + +UNSIGNED-NUMBER : + UNSIGNED-INTEGER | UNSIGNED-REAL + +type-specifier : + ["."] name + +name : + IDENT { "." IDENT } + +component-reference : + [ "." ] IDENT [ array-subscripts ] { "." IDENT [ array-subscripts ] } + +result-reference : + component-reference + | der "(" component-reference [ "," UNSIGNED-INTEGER ] ")" + +function-call-args : + "(" [ function-arguments ] ")" + +function-arguments : + expression [ "," function-arguments-non-first | for for-indices ] + | function-partial-application [ "," function-arguments-non-first ] + | named-arguments + +function-arguments-non-first : + function-argument [ "," function-arguments-non-first ] + | named-arguments + +array-arguments : + expression [ "," array-arguments-non-first | for for-indices ] + +array-arguments-non-first : + expression [ "," array-arguments-non-first ] + +named-arguments: named-argument [ "," named-arguments ] + +named-argument: IDENT "=" function-argument + +function-argument : + function-partial-application | expression + +function-partial-application : + function type-specifier "(" [ named-arguments ] ")" + +output-expression-list : + [ expression ] { "," [ expression ] } + +expression-list : + expression { "," expression } + +array-subscripts : + "[" subscript { "," subscript } "]" + +subscript : + ":" | expression + +description : + description-string [ annotation-clause ] + +description-string : + [ STRING { "+" STRING } ] + +annotation-clause : + annotation class-modification +---- \ No newline at end of file diff --git a/docs/B___dae.adoc b/docs/B___dae.adoc new file mode 100644 index 000000000..0d598a3c1 --- /dev/null +++ b/docs/B___dae.adoc @@ -0,0 +1,207 @@ +[appendix] +== Modelica DAE Representation + +In this appendix, the mapping of a Modelica model into an appropriate mathematical description form is discussed. + +In a first step, a Modelica translator transforms a hierarchical Modelica simulation model into a "flat" set of Modelica "statements", consisting of the equation and algorithm sections of all used components by: + +* Expanding all class definitions (flattening the inheritance tree) and adding the equations and assignment statements of the expanded classes for every instance of the model. + +* Replacing all `connect`-equations by the corresponding equations of the connection set (see <>). + +* Mapping all algorithm sections to equation sets. + +* Mapping all `when`-clauses to equation sets (see <>). + +[[eq:hybrid-dae,B.1]] +As a result of this transformation process, a set of equations is obtained consisting of differential, algebraic and discrete equations of the following form where (latexmath:[v := \lbrack p; t; \dot{x}; x; y; z; m; \text{pre}(z); \text{pre}(m)\rbrack]): + +.B.1a +[[eq:dae,B.1a]] +[latexmath] +++++ +0 = f_{\mathrm{x}}(v, c) +++++ + +.B.1b +[[eq:dae-discrete-real,B.1b]] +[latexmath] +++++ +z = +\begin{cases} +f_{\mathrm{z}}(v, c) & \text{at events} \\ +\text{pre}(z) & \text{otherwise} +\end{cases} +++++ + +.B.1c +[[eq:dae-discrete-valued,B.1c]] +[latexmath] +++++ +m := f_{\mathrm{m}}(v, c) +++++ + +.B.1d +[[eq:crossing,B.1d]] +[latexmath] +++++ +c := f_{\mathrm{c}}(\mathit{relation}(v)) +++++ + +and where + +* _p_: + Modelica variables declared as `parameter` or `constant`, i.e., variables without any time-dependency. + +* _t_: + Modelica variable `time`, the independent (real) variable. + +* _x(t)_: + Modelica variables of type `Real`, appearing differentiated. + +* _y(t)_: + Continuous-time modelica variables of type `Real` that do not appear differentiated (= algebraic variables). + +* _z(t~e~)_: + Discrete-time modelica variables of type `Real`. + These variables change their value only at event instants _t~e~_. + `pre(z)` are the values of _z_ immediately before the current event occurred. + +* _m(t~e~)_: + Modelica variables of discrete-valued types (`Boolean`, `Integer`, etc) which are unknown. + These variables change their value only at event instants _t~e~_. + `pre(m)` are the values of _m_ immediately before the current event occurred. ++ +[NOTE] +==== +For equations in `when`-clauses with discrete-valued variables on the left-hand side, the form (<>) relies upon the conceptual rewriting of equations described in <>. +==== + +* _c(t~e~)_: + The conditions of all `if`-expressions generated including `when`-clauses after conversion, see <>). + +* relation(_v_): + A relation containing variables _v~i~_, e.g., _v~1~_ > _v~2~_, _v~3~_ ≥ 0. + +For simplicity, the special cases of `noEvent` and `reinit` are not contained in the equations above and are not discussed below. + +The key difference between the two groups of discrete-time variables _z_ and and _m_ here is how they are determined. +The interpretation of the solved form of (<>) is that given values for everything else, there is a closed-form solution for _m_ in the form of a sequence of assignments to each of the variables of _m_ in turn -- there must be no cyclic dependencies between the equations used to solve for _m_. +Further, each of the original model equations behind (<>) must be given in almost solved form: + +* Non-`Integer` equations at most requiring flipping sides of the equation to obtain the used assignment form. +* For `Integer` equations the solved variable must appear uniquely as a term (without any multiplicative factor) on either side of the equation, at most requiring addition or subtraction of other terms in the equation to obtain the used assignment form. + +The interpretation of the non-solved form of (<>) at events, on the other hand, is that at events, the discrete-time `Real` variables _z_ are solved together with the continuous-time variables using (<>) and (<>). + +[example] +==== +Example: The following model demonstrates that equation (<>) does not imply that all discrete-time `Real` variables are given by equations in solved form, as also the discrete-time `Real` variables are included in _z_: + +[source,modelica] +---- +model M + discrete Real x(start = 1.0, fixed = true); +equation + when sample(1.0, 1.0) then + x = 3 * pre(x) - x^2; // Valid equation for discrete-time Real variable x. + end when; +end M; +---- + +Another way that a discrete-time `Real` variable can end up becoming determined by a nonlinear equation is through coupling with other variables. + +[source,modelica] +---- +model M + discrete Real x(start = 1.0, fixed = true); + discrete Real y(start = 0.0, fixed = true); +equation + when sample(1.0, 1.0) then + y = x ^ 2 + 2 * exp(-time); + x = 3 * pre(x) - y; // OK, forming nonlinear equation system with y. + end when; +end M; +---- +==== + +[example] +==== +Example: The following model is illegal since there is no equation in solved form that can be used in (<>) to solve for the discrete-valued variable `y`: + +[source,modelica] +---- +model M + Boolean x; + Boolean y; +equation + x = time >= 1.0; + not y = x; /* Equation in solved form, but not with respect to y. */ +end M; +---- +==== + +The generated set of equations is used for simulation and other analysis activities. +Simulation proceeds as follows. +First, initialization takes place, during which initial values for the states _x_ are found, <>. +Given those initial values the equations are simulated forward in time; this is the transient analysis. +The equations define a DAE (Differential Algebraic Equations) which may have discontinuities, a variable structure and/or which are controlled by a discrete-event system. +Such types of systems are called _hybrid DAEs_. +After initialization, simulation proceeds with transient analysis in the following way: + +. The DAE (<>) is solved by a numerical integration method. + In this phase the conditions _c_ of the `if`- and `when`-clauses, as well as the discrete-time variables _z_ and _m_ are kept constant. + Therefore, (<>) is a continuous function of continuous variables and the most basic requirement of numerical integrators is fulfilled. + +. During integration, all relations from (<>) are monitored. + If one of the relations changes its value an event is triggered, i.e., the exact time instant of the change is determined and the integration is halted. + As discussed in <>, relations which depend only on time are usually treated in a special way, because this allows determining the time instant of the next event in advance. + +. At an event instant, (<>) is a mixed set of algebraic equations which is solved for the `Real`, `Boolean` and `Integer` unknowns. + +. After an event is processed, the integration is restarted at phase 1. + +Note, that both the values of the conditions _c_ as well as the values of _z_ and _m_ (all discrete-time `Real`, `Boolean` and `Integer` variables) are only changed at an event instant and that these variables remain constant during continuous integration. +At every event instant, new values of the discrete-time variables _z_ and _m_, as well as of new initial values for the states _x_, are determined. +The change of discrete-time variables may characterize a new structure of a DAE where elements of the state vector _x_ are _disabled_. +In other words, the number of state variables, algebraic variables and residue equations of a DAE may change at event instants by disabling the appropriate part of the DAE. +For clarity of the equations, this is not explicitly shown by an additional index in (<>). + +At an event instant, including the initial event, the model equations are reinitialized according to the following iteration procedure: + +[source,modelica] +---- +known variables: x, t, p +unkown variables: dx/dt, y, z, m, pre(z), pre(m), c + +// pre(z) = value of z before event occured +// pre(m) = value of m before event occured +loop + solve (<>) for the unknowns, with pre(z) and pre(m) fixed + if z == pre(z) and m == pre(m) then break + pre(z) := z + pre(m) := m +end loop +---- + +Clocked variables are handled similarly as _z_ and _m_ (depending on type), but using `previous` instead of `pre` and only solved in the first event iteration. + +Solving (<>) for the unknowns is non-trivial, because this set of equations contains not only `Real`, but also discrete-valued unknowns. +Usually, in a first step these equations are sorted and in many cases the discrete-valued unknowns _m_ can be just computed by a forward evaluation sequence. +In some cases, there remain systems of equations involving _m_ due to cyclic dependencies with _y_ and _z_ (e.g., for ideal diodes, Coulomb friction elements), and specialized algorithms have to be used to solve them. + +Due to the construction of the equations by _flattening_ a Modelica model, the hybrid DAE (<>) contains a huge number of sparse equations. +Therefore, direct simulation of (<>) requires sparse matrix methods. +However, solving this initial set of equations directly with a numerical method is both unreliable and inefficient. +One reason is that many Modelica models, like the mechanical ones, have a DAE index of 2 or 3, i.e., the overall number of states of the model is less than the sum of the states of the sub-components. +In such a case, every direct numerical method has the difficulty that the numerical condition becomes worse, if the integrator step size is reduced and that a step size of zero leads to a singularity. +Another problem is the handling of idealized elements, such as ideal diodes or Coulomb friction. +These elements lead to mixed systems of equations having both `Real` and `Boolean` unknowns. +Specialized algorithms are needed to solve such systems. + +To summarize, symbolic transformation techniques are needed to transform (<>) into a set of equations which can be numerically solved reliably. +Most important, the algorithm of Pantelides should to be applied to differentiate certain parts of the equations in order to reduce the index. +Note, that also explicit integration methods, such as Runge-Kutta algorithms, can be used to solve (<>), after the index of (<>) has been reduced by the Pantelides algorithm: During continuous integration, the integrator provides _x_ and _t_. +Then, (<>) is a linear or nonlinear system of equations to compute the algebraic variables _y_ and the state derivatives latexmath:[\frac{dx}{dt}] and the model returns latexmath:[\frac{dx}{dt}] to the integrator by solving these systems of equations. +Often, (<>) is just a linear system of equations in these unknowns, so that the solution is straightforward. +This procedure is especially useful for real-time simulation where usually explicit one-step methods are used. \ No newline at end of file diff --git a/docs/C___derivationofstream.adoc b/docs/C___derivationofstream.adoc new file mode 100644 index 000000000..cd871a281 --- /dev/null +++ b/docs/C___derivationofstream.adoc @@ -0,0 +1,302 @@ +[appendix] +== Derivation of Stream Equations +:id: derivation-of-stream-equations + +This appendix contains a derivation of the equation for stream connectors from <>. + +=== Mixing Enthalpy +:id: mixing-enthalpy + +Consider a connection set with _n_ connectors, and denote the mass flow rates `m_flow` by latexmath:[\tilde{m}]. +The mixing enthalpy is defined by the mass balance (the general mass-balance for a component has latexmath:[\dot{m}=\sum\tilde{m}] which simplifies for the mixing enthalpy where latexmath:[m=0] and thus latexmath:[\dot{m}=0]): + +[latexmath] +++++ +0 = \sum_{j=1}^n \tilde{m}_j +++++ + +and similarly the energy balance: + +[latexmath] +++++ +0 = \sum_{j=1}^n \tilde{H}_j +++++ + +with + +[latexmath] +++++ +\tilde{H}_j = \tilde{m}_j +\begin{cases} +h_{\mathrm{mix}} & \text{if } \tilde{m}_j > 0 \\ +h_{\mathrm{outflow},j} & \text{if } \tilde{m}_j \leq 0 +\end{cases} +++++ + +Herein, mass flow rates are positive when entering models (exiting the connection set). +The specific enthalpy represents the specific enthalpy inside the component, close to the connector, for the case of outflow. +Expressed with variables used in the balance equations we arrive at: + +[latexmath] +++++ +h_{\mathrm{outflow},j} = +\begin{cases} +\frac{\tilde{H}_j}{\tilde{m}_j} & \text{if } \tilde{m}_j < 0 \\ +\textrm{arbitrary} & \text{if } \tilde{m}_j \geq 0 +\end{cases} +++++ + +While these equations are suitable for device-oriented modeling, the straightforward usage of this definition leads to models with discontinuous residual equations, which violates the prerequisites of several solvers for nonlinear equation systems. +This is the reason why the actual mixing enthalpy is not modelled directly in the model equations. +The stream connectors provide a suitable alternative. + +.Exemplary connection set with three connected components and a common mixing enthalpy. +image::media/fluidmix.svg[] + +=== Rationale for inStream +:id: rationale-for-instream + +For simplicity, the derivation of `inStream` is shown for 3 model components that are connected together. +The case for _N_ connections follows correspondingly. + +[[eq:D1,C.1]] +The energy and mass balance equations for the connection set for 3 components are (see above): + +[[eq:D1a,C.1a]] +.C.1a +[latexmath] +++++ +\begin{equation} +\begin{split} +0 = & \tilde{m}_1 \cdot +\begin{cases} +h_{\mathrm{mix}} & \text{if } \tilde{m}_1 > 0 \\ +h_{\mathrm{outflow},1} & \text{if } \tilde{m}_1 \leq 0 +\end{cases} \\ ++ & \tilde{m}_2 \cdot +\begin{cases} +h_{\mathrm{mix}} & \text{if } \tilde{m}_2 > 0 \\ +h_{\mathrm{outflow},2} & \text{if } \tilde{m}_2 \leq 0 +\end{cases} \\ ++ & \tilde{m}_3 \cdot +\begin{cases} +h_{\mathrm{mix}} & \text{if } \tilde{m}_3 > 0 \\ +h_{\mathrm{outflow},3} & \text{if } \tilde{m}_3 \leq 0 +\end{cases} +\end{split} +\end{equation} +++++ + +[[eq:D1b,C.1b]] +.C.1b +[latexmath] +++++ +\begin{equation} +0 = \tilde{m}_1 + \tilde{m}_2 + \tilde{m}_3 +\end{equation} +++++ + +[[eq:D2,C.2]] +The balance equations are implemented using a latexmath:[\operatorname{max}] operator in place of the piecewise expressions, taking care of the different flow directions: + +[[eq:D2a,C.2a]] +.C.2a +[latexmath] +++++ +\begin{equation} +\begin{split} +0 = & \operatorname{max}(\tilde{m}_1,0) h_{\mathrm{mix}} - \operatorname{max}(-\tilde{m}_1,0) h_{\mathrm{outflow},1} \\ ++ & \operatorname{max}(\tilde{m}_2,0) h_{\mathrm{mix}} - \operatorname{max}(-\tilde{m}_2,0) h_{\mathrm{outflow},2} \\ ++ & \operatorname{max}(\tilde{m}_3,0) h_{\mathrm{mix}} - \operatorname{max}(-\tilde{m}_3,0) h_{\mathrm{outflow},3} +\end{split} +\end{equation} +++++ + +[[eq:D2b,C.2b]] +.C.2b +[latexmath] +++++ +\begin{equation} +\begin{split} +0 = & \operatorname{max}(\tilde{m}_1,0) - \operatorname{max}(-\tilde{m}_1,0) \\ ++ & \operatorname{max}(\tilde{m}_2,0) - \operatorname{max}(-\tilde{m}_2,0) \\ ++ & \operatorname{max}(\tilde{m}_3,0) - \operatorname{max}(-\tilde{m}_3,0) +\end{split} +\end{equation} +++++ + +Equation (<>) is solved for latexmath:[h_{\mathrm{mix}}]: + +[latexmath] +++++ +h_{\mathrm{mix}} = \frac{ + \operatorname{max}(-\tilde{m}_1,0) h_{\mathrm{outflow},1} ++ \operatorname{max}(-\tilde{m}_2,0) h_{\mathrm{outflow},2} ++ \operatorname{max}(-\tilde{m}_3,0) h_{\mathrm{outflow},3} +}{ + \operatorname{max}(\tilde{m}_1,0) ++ \operatorname{max}(\tilde{m}_2,0) ++ \operatorname{max}(\tilde{m}_3,0) +} +++++ + +Using (<>), the denominator can be changed to: + +[latexmath] +++++ +h_{\mathrm{mix}} = \frac{ + \operatorname{max}(-\tilde{m}_1,0) h_{\mathrm{outflow},1} ++ \operatorname{max}(-\tilde{m}_2,0) h_{\mathrm{outflow},2} ++ \operatorname{max}(-\tilde{m}_3,0) h_{\mathrm{outflow},3} +}{ + \operatorname{max}(-\tilde{m}_1,0) ++ \operatorname{max}(-\tilde{m}_2,0) ++ \operatorname{max}(-\tilde{m}_3,0) +} +++++ + +Above it was shown that an equation of this type does not yield properly formulated model equations. +In the streams concept we therefore decide to split the energy balance, which consists of different branches depending on the mass flow direction. +Consequently, separate energy balances are the result; each valid for specific flow directions. + +In a model, governing equations have to establish the specific enthalpy of fluid leaving the model based on the specific enthalpy of fluid flowing into it. +Whenever the mixing enthalpy is _used_ in a model it is therefore the mixing enthalpy under the assumption of fluid flowing into said model. + +We establish this quantity using a dedicated operator `inStream(h_outflow,i) = h_mix` assuming that latexmath:[\tilde{m}_i \geq 0]. +This leads to different incarnations of latexmath:[h_{\mathrm{mix}}], three in this case and latexmath:[n] in the general case. +This is illustrated in the figure below. +For the present example of three components in a connection set, this means the following: + +[latexmath] +++++ +\begin{align*} +\text{inStream}(h_{\mathrm{outflow},1}) &= \frac{\operatorname{max}(-\tilde{m}_2,0) h_{\mathrm{outflow},2} + \operatorname{max}(-\tilde{m}_3,0) h_{\mathrm{outflow},3}}{\operatorname{max}(-\tilde{m}_2,0) + \operatorname{max}(-\tilde{m}_3,0)} \\ +\text{inStream}(h_{\mathrm{outflow},2}) &= \frac{\operatorname{max}(-\tilde{m}_1,0) h_{\mathrm{outflow},1} + \operatorname{max}(-\tilde{m}_3,0) h_{\mathrm{outflow},3}}{\operatorname{max}(-\tilde{m}_1,0) + \operatorname{max}(-\tilde{m}_3,0)} \\ +\text{inStream}(h_{\mathrm{outflow},3}) &= \frac{\operatorname{max}(-\tilde{m}_1,0) h_{\mathrm{outflow},1} + \operatorname{max}(-\tilde{m}_2,0) h_{\mathrm{outflow},2}}{\operatorname{max}(-\tilde{m}_1,0) + \operatorname{max}(-\tilde{m}_2,0)} +\end{align*} +++++ + +.Exemplary connection set with three connected components. +image::media/fluidmix3.svg[] + +In the general case of a connection set with _n_ components, similar considerations lead to the following: + +[latexmath] +++++ +\text{inStream}(h_{\mathrm{outflow},i}) = \frac{\sum_{j=1,\dotsc,n; j \neq i} \operatorname{max}(-\tilde{m}_j,0) h_{\mathrm{outflow},j}}{\sum_{j=1,\dotsc,n; j \neq i} \operatorname{max}(-\tilde{m}_j,0)} +++++ + +=== Special Cases Covered by inStream Definition +:id: special-cases-covered-by-the-instream-operator-definition + +==== Unconnected Stream Connector -- 1 Stream Connector +:id: unconnected-stream-connector-1-stream-connector + +For this case, the return value of `inStream` is arbitrary. +Therefore, it is set to the outflow value. + +==== One to One Connections -- Connection of 2 Stream Connectors +:id: one-to-one-connections-connection-of-2-stream-connectors + +[latexmath] +++++ +\begin{align*} +\text{inStream}(h_{\mathrm{outflow},1}) &= \frac{\operatorname{max}(-\tilde{m}_2,0) h_{\mathrm{outflow},2}}{\operatorname{max}(-\tilde{m}_2,0)} = h_{\mathrm{outflow},2} \\ +\text{inStream}(h_{\mathrm{outflow},2}) &= \frac{\operatorname{max}(-\tilde{m}_1,0) h_{\mathrm{outflow},1}}{\operatorname{max}(-\tilde{m}_1,0)} = h_{\mathrm{outflow},1} +\end{align*} +++++ + +In this case, `inStream` is continuous (contrary to latexmath:[h_{\mathrm{mix}}]) and does not depend on flow rates. +The latter result means that this transformation may remove nonlinear systems of equations, which requires that either simplifications of the form latexmath:[a * b / a = b] must be provided, or that this case is treated directly. + +==== Zero Mass Flow Rate -- Connection of 3 Stream Connectors +:id: zero-mass-flow-rate-connection-of-3-stream-connectors + +The case where latexmath:[N=3] and latexmath:[\tilde{m}_3=0] occurs when a one-port sensor (like a temperature sensor) is connected to two other components. +For the sensor, the `min` attribute of the mass flow rate should be set to zero (no fluid exiting the component via this connector). +This simplification (and similar ones) can also be used if a tool determines that a mass flow rate is zero or non-negative. +It is also possible to generalize this to the case where more than one sensor is connected. +The suggested implementation results in the following equations, and as indicated the last formula can be simplified further by using latexmath:[\tilde{m}_3=0]: + +[latexmath] +++++ +\begin{align*} +\text{inStream}(h_{\mathrm{outflow},1}) &= h_{\mathrm{outflow},2} \\ +\text{inStream}(h_{\mathrm{outflow},2}) &= h_{\mathrm{outflow},1} \\ +\text{inStream}(h_{\mathrm{outflow},3}) &= \frac{\operatorname{max}(-\tilde{m}_1,0) h_{\mathrm{outflow},1} + \operatorname{max}(-\tilde{m}_2,0) h_{\mathrm{outflow},2}}{\operatorname{max}(-\tilde{m}_1,0) + \operatorname{max}(-\tilde{m}_2,0)} \\ +&= +\begin{cases} +h_{\mathrm{outflow},2} & \text{if } \tilde{m}_1 \geq 0 \\ +h_{\mathrm{outflow},1} & \text{if } \tilde{m}_1 < 0 \text{ and } \tilde{m}_3 = 0 +\end{cases} +\end{align*} +++++ + +.Example series connection of multiple models with stream connectors. +image::media/fluidmix4.svg[] + +For the two components with finite mass flow rates (not the sensor), the properties discussed for two connected components still hold. +The connection set equations reflect that the sensor does not have any influence by discarding the flow rate of the latter. +In several cases a non-linear equation system is removed by this transformation. +However, `inStream` results in a discontinuous equation for the sensor, which is consistent with modeling the convective phenomena only. +The discontinuous equation is uncritical, if the sensor variable is not used in a feedback loop with direct feedthrough, since the discontinuous equation is then not part of an algebraic loop. +Otherwise, it is advisable to regularize or filter the sensor signal. + +==== Ideal Splitting Junction for Uni-Directional Flow - Connection of 3 Stream Connectors where Two Mass Flow Rates are Positive +:id: ideal-splitting-junction-for-uni-directional-flow-connection-of-3-stream-connectors-where-two-mass-flow-rates-are-positive + +If uni-directional flow is present and an ideal splitter is modelled, the required flow direction should be defined in the connector instance with the `min` attribute (the `max` attribute could be also defined, however it does not lead to simplifications): + +[source,modelica] +---- +model m2 + Fluidport c(m_flow(min=0)); + ... +end m2; +---- + +Consider the case of latexmath:[\tilde{m}_{1} < 0] and all other mass flow rates positive (with the `min` attribute set accordingly). +Connecting `m1.c` with `m2.c` and `m3.c`, such that + +[source,modelica] +---- +m2.c.m_flow.min = 0; // max(-m2.c.m_flow,0) = 0 +m3.c.m_flow.min = 0; // max(-m3.c.m_flow,0) = 0 +---- + +results in the following equation: + +[latexmath] +++++ +\text{inStream}(h_{\mathrm{outflow},1}) = \frac{\operatorname{max}(-\tilde{m}_2,0) h_{\mathrm{outflow},2} + \operatorname{max}(-\tilde{m}_3,0) h_{\mathrm{outflow},3}}{\operatorname{max}(-\tilde{m}_2,0) + \operatorname{max}(-\tilde{m}_3,0)} = \frac{0}{0} +++++ + +`inStream` cannot be evaluated for a connector, on which the mass flow rate has to be negative by definition. +This is not a problem since there is no requirement on the result of `inStream` in this case, and the following result is selected instead of the illegal division: + +[latexmath] +++++ +\text{inStream}(h_{\mathrm{outflow},1}) := h_{\mathrm{outflow},1} +++++ + +For the remaining connectors, `inStream` reduces to a simple result. + +[latexmath] +++++ +\begin{align*} +\text{inStream}(h_{\mathrm{outflow},2}) &= \frac{\operatorname{max}(-\tilde{m}_1,0) h_{\mathrm{outflow},1} + \operatorname{max}(-\tilde{m}_3,0) h_{\mathrm{outflow},3}}{\operatorname{max}(-\tilde{m}_1,0) + \operatorname{max}(-\tilde{m}_3,0)} + = h_{\mathrm{outflow},1} \\ +\text{inStream}(h_{\mathrm{outflow},3}) &= \frac{\operatorname{max}(-\tilde{m}_1,0) h_{\mathrm{outflow},1} + \operatorname{max}(-\tilde{m}_2,0) h_{\mathrm{outflow},2}}{\operatorname{max}(-\tilde{m}_1,0) + \operatorname{max}(-\tilde{m}_2,0)} + = h_{\mathrm{outflow},1} +\end{align*} +++++ + +Again, the previous non-linear algebraic system of equations is removed. +This means that utilizing the information about uni-directional flow is very important. + +To summarize, if all mass flow rates are zero, the balance equations for stream variables (<>) and for flows (<>) are identically fulfilled. +In such a case, any value of latexmath:[h_{\mathrm{mix}}] fulfills (<>), i.e., a unique mathematical solution does not exist. +This specification only requires that a solution fulfills the balance equations. +Additionally, a recommendation is given to compute all unknowns in a unique way, by providing an explicit formula for `inStream`. +Due to the definition, that only flows where the corresponding `min`-attribute is neither zero nor positive enter this formula, a meaningful physical result is always obtained, even in case of zero mass flow rate. +As a side effect, non-linear equation systems are automatically removed in special cases, like sensors or uni-directional flow, without any symbolic transformations (no need to analyze any equation; only the `min`-attributes of the corresponding flow variables). \ No newline at end of file diff --git a/docs/D___revisions.adoc b/docs/D___revisions.adoc new file mode 100644 index 000000000..7eb569471 --- /dev/null +++ b/docs/D___revisions.adoc @@ -0,0 +1,13 @@ +[appendix] +== Modelica Revision History +:id: modelica-revision-history + +This appendix describes the history of the Modelica Language Design, and its contributors. +This appendix is just present for historical reasons and is not normative. +The current version, as well as all previous versions of this document, and the list of changes are available at https://specification.modelica.org. + +The members of the Modelica Association project _Modelica Language_ (MAP-Lang) contributed to the Modelica specification. + +Keeping just the start to have a historic perspective: + +Modelica 1, the first version of Modelica, was released in September 1997, and had the language specification as a short appendix to the rationale. \ No newline at end of file diff --git a/docs/E___bibliography.adoc b/docs/E___bibliography.adoc new file mode 100644 index 000000000..707272045 --- /dev/null +++ b/docs/E___bibliography.adoc @@ -0,0 +1,32 @@ +[bibliography] +== Bibliography + +[[BenvenisteEtAl2003SynchronousTwelveYearsLater,Benveniste, Caspi, Edwards, Halbwachs, Le Guernic, and Simone 2003]] +Benveniste, Albert; Caspi, Paul; Edwards, Stephen A.; Halbwachs, Nicolas; Le Guernic, Paul; Simone, Robert de (2003). "The Synchronous Languages Twelve Years Later." In: _Proceedings of the IEEE_, 91(1). https://doi.org/10.1109/JPROC.2002.805826 (cit. on p. 240). + +[[Buerger2019SelectiveModel,Bürger 2019]] +Bürger, Christoff (2019). "Modelica language extensions for practical non-monotonic modelling: on the need for _selective_ model extension." In: _Proceedings of the 13th International Modelica Conference_, Regensburg, Germany, Mar 4-6, pp. 277-288. https://doi.org/10.3384/ecp19157277 + +[[ColacoPouzet2003ClocksFirstClass,Colaço and Pouzet 2003]] +Colaço, Jean-Louis; Pouzet, Marc (2003). "Clocks as First Class Abstract Types." In: _Third International Workshop on Embedded Software, EMSOFT 2003_, Philadelphia, Pennsylvania, USA. https://doi.org/10.1007/978-3-540-45212-6_10. http://www.di.ens.fr/~pouzet/lucid-synchrone/papers/emsoft03.ps.gz + +[[GettextManual,Drepper, Meyering, Pinard, and Haible 2020]] +Drepper, Ulrich; Meyering, Jim; Pinard, François; Haible, Bruno (2020). "GNU gettext tools, version 0.21." https://www.gnu.org/software/gettext/manual/ + +[[ElmqvistOtterCellier1995InlineIntegration,Elmqvist, Otter, and Cellier 1995]] +Elmqvist, Hilding; Otter, Martin; Cellier, Francois E. (1995). "Inline Integration: A New Mixed Symbolic/Numeric Approach for Solving Differential-Algebraic Equation Systems." In: _Proceedings of ESM’95, European Simulation Multiconference_, Prague, Czech Republic, June 5-8, pp. xxiii-xxxiv. https://www.semanticscholar.org/paper/Inline-Integration%3A-A-New-Mixed-Symbolic%2FNumeric-Elmqvist-Otter/b696154cbfb9c82dd4983abbd45ed639a4d5c32c + +[[ForgetEtAl2008MultiPeriodic,Forget, Boniol, Lesens, and Pagetti 2008]] +Forget, Julien; Boniol, Frédéric; Lesens, David; Pagetti, Claire (2008). "A Multi-Periodic Synchronous Data-Flow Language." In: _11th IEEE High Assurance Systems Engineering Symposium (HASE'08)_, Nanjing, China, Dec 3-5, pp. 251-260. https://doi.org/10.1109/HASE.2008.47 + +[[Harel1987Statecharts,Harel 1987]] +Harel, David (1987). "Statecharts: A Visual Formalism for Complex Systems." _Science of Computer Programming_, 8, pp. 231-274. https://doi.org/10.1016/0167-6423(87)90035-9 + +[[Pantelides1988ConsistentInitialization,Pantelides 1988]] +Pantelides, Constantinos C. (1988). "The Consistent Initialization of Differential-Algebraic Systems." _SIAM Journal on Scientific and Statistical Computing_, 9(2), pp. 213-231. https://doi.org/10.1137/0909014 + +[[Pouzet2006LucidSynchrone30,Pouzet 2006]] +Pouzet, Marc (2006). "Lucid Synchrone, Version 3.0, Tutorial and Reference Manual." http://www.di.ens.fr/~pouzet/lucid-synchrone/ + +[[ThummelEtAl2005InverseModels,Thümmel, Looye, Kurze, Otter, and Bals (2005)]] +Thümmel, Michael; Looye, Gertjan; Kurze, Matthias; Otter, Martin; Bals, Johann (2005). "Nonlinear Inverse Models for Control." In: _Proceedings of 4th International Modelica Conference_, ed. G. Schmitz, Hamburg, Germany, Mar 7-8. https://modelica.org/events/Conference2005/online_proceedings/Session3/Session3c3.pdf \ No newline at end of file diff --git a/docs/common/css/light.css b/docs/common/css/light.css new file mode 100644 index 000000000..e69de29bb diff --git a/docs/common/js/color-theme.js b/docs/common/js/color-theme.js new file mode 100644 index 000000000..8c56ccb49 --- /dev/null +++ b/docs/common/js/color-theme.js @@ -0,0 +1,70 @@ +window.addEventListener('DOMContentLoaded', () => { + + var theme = localStorage.getItem('theme'); + + if (theme != 'light' && theme != 'dark') { + theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; + localStorage.setItem('theme', theme); + } + + document.documentElement.setAttribute('data-ma-theme', theme); + + updateTheme(theme); +}) + +window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { + const newColorScheme = event.matches ? "dark" : "light"; + updateTheme(newColorScheme); +}); + +function updatePictureSrc(picture, theme) { + + const sources = picture.querySelectorAll('source'); + + sources.forEach(source => { + + const media = source.getAttribute('media'); + + dataTheme = source.getAttribute('data-theme'); + + if (!dataTheme && media) { + if (media.includes('prefers-color-scheme: dark')) { + source.setAttribute('data-theme', 'dark'); + source.setAttribute('media', 'not all'); + } else if (media.includes('prefers-color-scheme: light')) { + source.setAttribute('data-theme', 'light'); + source.setAttribute('media', 'not all'); + } + } + + dataTheme = source.getAttribute('data-theme'); + + if (dataTheme) { + if (dataTheme === theme) { + source.setAttribute('media', '(prefers-color-scheme: light),(prefers-color-scheme: dark)'); + } else { + source.setAttribute('media', 'not all'); + } + } + }); +} + +function updateTheme(newTheme) { + + localStorage.setItem('theme', newTheme); + + document.documentElement.setAttribute('data-ma-theme', newTheme); + + document.querySelectorAll('picture').forEach(picture => { + updatePictureSrc(picture, newTheme); + }); +} + +function toggleTheme() { + + var currentTheme = document.documentElement.getAttribute('data-ma-theme'); + + var newTheme = currentTheme == 'dark' ? 'light' : 'dark'; + + updateTheme(newTheme); +} diff --git a/docs/common/js/scrollspy.js b/docs/common/js/scrollspy.js new file mode 100644 index 000000000..99fc49a40 --- /dev/null +++ b/docs/common/js/scrollspy.js @@ -0,0 +1,64 @@ +const toc = document.querySelector("#toc"); + +const sectionLinkNodes = toc.querySelectorAll( + ".sectlevel1 > li > a, .sectlevel2 > li > a, .sectlevel3 > li > a, .sectlevel4 > li > a, .sectlevel5 > li > a" +); + +var sectionLinks = {}; + +sectionLinkNodes.forEach(function(sectionLinkNode) { + sectionLinks[sectionLinkNode.hash] = sectionLinkNode; +}); + +var sections = []; + +sections = [ +...document.querySelectorAll('.sect5'), +...document.querySelectorAll('.sect4'), +...document.querySelectorAll('.sect3'), +...document.querySelectorAll('.sect2'), +...document.querySelectorAll('.sect1'), +]; + +window.addEventListener('scroll', function () { + + const currentPos = window.scrollY + 80; + + for (var i = 0; i < sections.length; i++) { + + const section = sections[i]; + + const sectionTop = section.offsetTop + section.offsetParent.offsetTop; + const sectionHeight = section.offsetHeight; + const sectionId = section.getAttribute('id'); + + if (currentPos >= sectionTop && currentPos < sectionTop + sectionHeight) { + + sectionLinkNodes.forEach(function (navbarLink) { + navbarLink.classList.remove('active'); + }); + + const reference = '#' + section.children[0].id; + + if (reference in sectionLinks) { + + currentSectionLink = sectionLinks[reference]; + + currentSectionLink.classList.add('active'); + + currentSectionLink.scrollIntoView({ + behavior: 'smooth', + block: 'nearest', + inline: 'nearest' + }); + + } else { + console.warn('Reference not found: ' + reference); + } + + break; + + } + } + +}); \ No newline at end of file diff --git a/docs/images/circle-half.svg b/docs/images/circle-half.svg new file mode 100644 index 000000000..0eb6514c2 --- /dev/null +++ b/docs/images/circle-half.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/docs/images/dark/Modelica-Language.svg b/docs/images/dark/Modelica-Language.svg new file mode 100644 index 000000000..59fc1960d --- /dev/null +++ b/docs/images/dark/Modelica-Language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/favicon.svg b/docs/images/favicon.svg new file mode 100644 index 000000000..c40aa896e --- /dev/null +++ b/docs/images/favicon.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/layout-sidebar.svg b/docs/images/layout-sidebar.svg new file mode 100644 index 000000000..2ea52580a --- /dev/null +++ b/docs/images/layout-sidebar.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/docs/images/light/Modelica-Language.svg b/docs/images/light/Modelica-Language.svg new file mode 100644 index 000000000..d13366c7a --- /dev/null +++ b/docs/images/light/Modelica-Language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/m-ball.svg b/docs/images/m-ball.svg new file mode 100644 index 000000000..993c564cc --- /dev/null +++ b/docs/images/m-ball.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/tag.svg b/docs/images/tag.svg new file mode 100644 index 000000000..5dc59a524 --- /dev/null +++ b/docs/images/tag.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/index.adoc b/docs/index.adoc new file mode 100644 index 000000000..85e4f0193 --- /dev/null +++ b/docs/index.adoc @@ -0,0 +1,461 @@ += Modelica Language Specification +:sectnums: +:sectnumlevels: 4 +// :sectlinks: +:toc-title: Contents +:toc: left +:toclevels: 4 +:idprefix: +:idseparator: - +:xrefstyle: short +// :docinfodir: docs +:stem: latexmath +:source-highlighter: pygments +:nofooter: +:favicon: images/favicon.svg +// :icons: font +:subs=none + +++++ + + + + + + + + + +
+ + + + + + + + + + + +
+ +
+ Modelica Language Specification +
+ 3.7-dev +
+
+
+ +
+ +
+ + + + + Modelica Language + + +

Modelica® - A Unified Object-Oriented Language for Systems Modeling

+

Language Specification

+

Version +++++ +{revnumber} +++++ +

+

+++++ +{revdate} +++++ +

+

Modelica Association

+
+++++ + +include::docs/0___preface.adoc[] + +include::docs/1___introduction.adoc[] + +include::docs/2___lexicalstructure.adoc[] + +include::docs/3___operatorsandexpressions.adoc[] + +include::docs/4___classes.adoc[] + +include::docs/5___scoping.adoc[] + +include::docs/6___interface.adoc[] + +include::docs/7___inheritance.adoc[] + +include::docs/8___equations.adoc[] + +include::docs/9___connectors.adoc[] + +include::docs/10__arrays.adoc[] + +include::docs/11__statements.adoc[] + +include::docs/12__functions.adoc[] + +include::docs/13__packages.adoc[] + +include::docs/14__overloaded.adoc[] + +include::docs/15__stream.adoc[] + +include::docs/16__synchronous.adoc[] + +include::docs/17__statemachines.adoc[] + +include::docs/18__annotations.adoc[] + +include::docs/19__unitexpressions.adoc[] + +include::docs/20__library.adoc[] + +include::docs/A___syntax.adoc[] + +include::docs/B___dae.adoc[] + +include::docs/C___derivationofstream.adoc[] + +include::docs/D___revisions.adoc[] + +include::docs/E___bibliography.adoc[] \ No newline at end of file diff --git a/docs/media/Modelica_Language.svg b/docs/media/Modelica_Language.svg new file mode 100644 index 000000000..735c17424 --- /dev/null +++ b/docs/media/Modelica_Language.svg @@ -0,0 +1,75 @@ + +image/svg+xml \ No newline at end of file diff --git a/docs/media/Modelica_Language_dark.svg b/docs/media/Modelica_Language_dark.svg new file mode 100644 index 000000000..7a459d0b8 --- /dev/null +++ b/docs/media/Modelica_Language_dark.svg @@ -0,0 +1,75 @@ + +image/svg+xml \ No newline at end of file diff --git a/docs/media/bezierpoints.svg b/docs/media/bezierpoints.svg new file mode 100644 index 000000000..37e991d26 --- /dev/null +++ b/docs/media/bezierpoints.svg @@ -0,0 +1,270 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/clock.svg b/docs/media/clock.svg new file mode 100644 index 000000000..489d2fea8 --- /dev/null +++ b/docs/media/clock.svg @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/clocked.svg b/docs/media/clocked.svg new file mode 100644 index 000000000..db8eb457c --- /dev/null +++ b/docs/media/clocked.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/diagram_examples.png b/docs/media/diagram_examples.png new file mode 100644 index 000000000..275ca7e4f Binary files /dev/null and b/docs/media/diagram_examples.png differ diff --git a/docs/media/disabledparameter.png b/docs/media/disabledparameter.png new file mode 100644 index 000000000..a62b5a442 Binary files /dev/null and b/docs/media/disabledparameter.png differ diff --git a/docs/media/fluidmix.svg b/docs/media/fluidmix.svg new file mode 100644 index 000000000..2044a3fe8 --- /dev/null +++ b/docs/media/fluidmix.svg @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/fluidmix3.svg b/docs/media/fluidmix3.svg new file mode 100644 index 000000000..207fc14bc --- /dev/null +++ b/docs/media/fluidmix3.svg @@ -0,0 +1,526 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/fluidmix4.svg b/docs/media/fluidmix4.svg new file mode 100644 index 000000000..443ea96cd --- /dev/null +++ b/docs/media/fluidmix4.svg @@ -0,0 +1,1163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/fluidsystem.svg b/docs/media/fluidsystem.svg new file mode 100644 index 000000000..ed27ffc11 --- /dev/null +++ b/docs/media/fluidsystem.svg @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/hierarchical-statemachine.svg b/docs/media/hierarchical-statemachine.svg new file mode 100644 index 000000000..a59660b5f --- /dev/null +++ b/docs/media/hierarchical-statemachine.svg @@ -0,0 +1,2381 @@ + + + + + state1 + stateA + + + stateB + + + stateC + + + stateD + + + + + + + + + + + + + + + stateX + + + stateY + + + + + + + + + state2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/innerouterconnector.svg b/docs/media/innerouterconnector.svg new file mode 100644 index 000000000..3b8195919 --- /dev/null +++ b/docs/media/innerouterconnector.svg @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/modelicapath.svg b/docs/media/modelicapath.svg new file mode 100644 index 000000000..dece5a626 --- /dev/null +++ b/docs/media/modelicapath.svg @@ -0,0 +1,917 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/overdetermined.svg b/docs/media/overdetermined.svg new file mode 100644 index 000000000..eb77e28c4 --- /dev/null +++ b/docs/media/overdetermined.svg @@ -0,0 +1,921 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/piecewise-constant.svg b/docs/media/piecewise-constant.svg new file mode 100644 index 000000000..e0e29c28a --- /dev/null +++ b/docs/media/piecewise-constant.svg @@ -0,0 +1,368 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/plantmodel.svg b/docs/media/plantmodel.svg new file mode 100644 index 000000000..75e3c7270 --- /dev/null +++ b/docs/media/plantmodel.svg @@ -0,0 +1,1655 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/statemachine.svg b/docs/media/statemachine.svg new file mode 100644 index 000000000..f7c64cc40 --- /dev/null +++ b/docs/media/statemachine.svg @@ -0,0 +1,226 @@ + + + + + + state1 + + state2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/statemachineplot.svg b/docs/media/statemachineplot.svg new file mode 100644 index 000000000..707c7ae0e --- /dev/null +++ b/docs/media/statemachineplot.svg @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/subtype.svg b/docs/media/subtype.svg new file mode 100644 index 000000000..2680c3cd4 --- /dev/null +++ b/docs/media/subtype.svg @@ -0,0 +1,509 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/media/tabparameter.png b/docs/media/tabparameter.png new file mode 100644 index 000000000..f934683bf Binary files /dev/null and b/docs/media/tabparameter.png differ