Modelica® - A Unified Object-Oriented Language for Systems Modeling Language Specification Version 3.4

Chapter 4 Classes, 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).

4.1 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 may 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 may 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

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 section 7.1.2.

4.2 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. A component shall not have the same name as its type specifier. 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 section 5.5, and section 7.3.

[Example:

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;

]

4.3 Declaration Order and Usage before Declaration

Variables and classes can be used before they are declared.

[In fact, declaration order is only significant for:

  • Functions with more than one input variable called with positional arguments, section 12.4.1.

  • Functions with more than one output variable, section 12.4.3.

  • Records that are used as arguments to external functions, section 12.9.1.3

  • Enumeration literal order within enumeration types, section 4.8.5.

]

4.4 Component Declarations

Component declarations are described in this section.

4.4.1 Syntax and Examples of Component Declarations

The formal syntax of a component declaration clause is given by the following syntactic rules:

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 ] comment
condition-attribute:
  if expression
declaration :
  IDENT [ array-subscripts ] [ modification ]

[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:

  Real[2,2] A, B;

That declaration has the same meaning as the following two declarations together:

  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:

  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:

  Real a, B[2,2];

]

4.4.2 Component Declaration 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.

If the type-specifier of the component does not denote a built-in type, the name of the type is looked up (section 5.3). 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 non-negative parameter expressions, or the colon operator denoting that the array dimension is left unspecified.

The rules for components in functions are described in section 12.2.

Conditional declarations of components are described in section 4.4.5.

4.4.2.1 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. For declarations of vectors and matrices, declaration equations are associated with each element.

4.4.2.2 Prefix Rules

Variables declared with the flow or the stream type prefix shall be a subtype of Real.

Type prefixes (that is , flow, stream, discrete, parameter, constant, input, output) shall only be applied for type, record and connector components – see also record specialized class, section 4.6.

An exception is input for components whose type is of the special class function type (these can only be used for function formal parameters and has special semantics, see section 12.4.2), and the input prefix is not applied to the elements of the component and is allowed even if the elements 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 section 12.9.7.

The type prefixes flow, stream, 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; e.g. the flow prefix can be used on a record component and all the record elements will generate zero-sum equations, even if elements of a record may not be declared with the flow prefix). When any of the type prefixes flow, stream, input and output are applied for a structured component, no element of the component may have any of these type prefixes. [For example, input can only be used, if none of the elements has a flow, stream, input or output type prefix]. The corresponding rules for the type prefixes discrete, parameter and constant are described in section 4.4.4.1 for structured components.

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 section 12.4.

  • 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 section 4.7.

  • 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 section 4.7. Example:

    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 section 9.1 (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 section 4.7 (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.

4.4.3 Acyclic Bindings of Constants and Parameters

The unexpanded binding equations for parameters and constants in the translated model must be acyclic after flattening; except that cycles are allowed if the cycles disappear when evaluating parameters having annotation Evaluate=true that are not part of the cycle. Thus it is not possible to introduce equations for parameters by cyclic dependencies.

[Example:

constant Real p=2*q;
constant Real q=sin(p); // Illegal since p=2*q, q=sin(p) are cyclical
model ABCD
  parameter Real A[n,n];
  parameter Integer n=size(A,1);
end ABCD;
final ABCD a;
// Illegal since cyclic dependencies 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.
parameter Real r = 2*sin(r); // Illegal, since r = 2*sin(r) is cyclic
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 ok since disappears
// when evaluating use_T_start
// Illegal since the unexpanded bindings have 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;

]

4.4.4 Component Variability Prefixes discrete, parameter, constant

The prefixes discrete, parameter, constant of a component declaration are called variability prefixes and define in which situation the variable values of a component are initialized (see section 8.5 and section 8.6) and when they are changed in transient analysis (= solution of initial value problem of the hybrid DAE):

  • A variable vc declared with the parameter or constant prefixes remains constant during transient analysis.

  • A discrete-time variable vd has a vanishing time derivative (informally der(vd)=, but it is not legal to apply the der() operator to discrete-time variables) and can change its values only at event instants during transient analysis (see section 8.5).

  • A continuous-time variable vn may have a non-vanishing time derivative (der(vn)<> possible) and may also change its value discontinuously at any time during transient analysis (see section 8.5). If there are any discontinuities the variable is not differentiable.

If a Real variable is declared with the prefix discrete it must in a simulation model be assigned in a when-clause, either by an assignment or an equation. The variable assigned in a when-clause may 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. [A Modelica translator is able to guarantee this property due to restrictions imposed on discrete expressions, see section 3.8]

The variability of expressions and restrictions on variability for definition equations is given in section 3.8.

[A discrete-time variable is a piecewise constant signal which changes its values only at event instants during simulation. Such types of variables are needed in order that special algorithms, such as the algorithm of Pantelides for index reduction, can be applied (it must be known that the time derivative of these variables is identical to zero). Furthermore, memory requirements can be reduced in the simulation environment, if it is known that a component can only change at event instants.

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-clause, a parameter prefix allows to remove 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:

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.

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.:

  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.

]

4.4.4.1 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:

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

]

4.4.5 Conditional Component Declaration

A component declaration can have a condition-attribute: ”ifexpression.

[Example:

  parameter Integer level(min=1)=1;
  Motor motor;
  Level1 component1(J=J) if level==1 "Conditional component";
  Level2 component2 if level==2 "Conditional component";
  Level3 component3(J=component1.J) if level<2 "Conditional component";
  // Illegal modifier on component3 since component1.J is conditional
  // Even if we can see that component1 always exist if component3 exist
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; // Illegal

]

The expression must be a Boolean scalar expression, and must be a parameter-expression [that can be evaluated at compile time].

A redeclaration of a component may not include a condition attribute; and the condition attribute is kept from the original declaration (see section 6.3).

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 and connections to/from the component are removed. [Adding the component and then removing it ensures that the component is valid.]A component declared with a condition-attribute can only be modified and/or used in connections [If a connect statement 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.]

If the condition is true for a public connector containing flow variables the connector must be connected from the outside. [The reason for this restriction is that the default flow equation is probably incorrect (since it could otherwise be an unconditional connector) and the model cannot check that connector is connected.]

4.5 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.

[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.

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.

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 string-comment composition end IDENT
  | extends IDENT [ class-modification ] string-comment composition
  end IDENT
short-class-specifier :
  IDENT "=" base-prefix name [ array-subscripts ]
  [ class-modification ] comment
  | IDENT "=" enumeration "(" ( [enum-list] | ":" ) ")" comment
der-class-specifier :
  IDENT "=" der "(" name "," IDENT { "," IDENT } ")" comment
base-prefix :
  [ input | output ]
enum-list : enumeration-literal { "," enumeration-literal}
enumeration-literal : IDENT comment
composition :
  element-list
  { public element-list |
    protected element-list |
    equation-section |
    algorithm-section
  }
  [ external [ language-specification ]
  [ external-function-call ] [ annotation ] ";" ]
  [ annotation ";" ]

4.5.1 Short Class Definitions

A class definition of the form

class IDENT1 = IDENT2 class-modification;

is identical, except that IDENT2 may be replaceable and for the lexical scope of modifiers, where the short class definition does not introduce an additional lexical scope for modifiers, to the longer form

class IDENT1
  extends IDENT2 class-modification;
end IDENT1;

[Example: demonstrating the difference in scopes:

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;
  Load a,b,c;
  ConstantSource ...;
  ...
end A;

]

A short class definition of the form

type TN = T[N] (optional modifier);

where N represents arbitrary array dimensions, conceptually yields an array class

’array’ TN
  T[n] _ (optional modifiers);
’end’ TN;

Such an array class has exactly one anonymous component (_); see also section 4.5.2. 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:

type Force = Real[3](unit={"Nm","Nm","Nm"});
Force f1;
Real f2[3](unit={"Nm","Nm","Nm"});

the types of f1 and f2 are identical.]

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 keyword partial or not.

[Example:

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 section 4.5.2.

[Example:

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);

]

4.5.2 Restriction on combining base-classes and other elements

It is not legal to combine other components or base-classes 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 (Real, Boolean, Integer, String and enumeration types).

[Example:

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;

]

4.5.3 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.

[The following example demonstrates parameterization of a local class:

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.

]

4.6 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 following table summarizes the definition of the specialized classes (additional restrictions on inheritance are in section 7.1.3):

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 may not have prefixes input, output, inner, outer, stream, or flow. Enhanced with implicitly available record constructor function, see section 12.6. Additionally, record components can be used as component references in expressions and in the left hand side of assignments, subject to normal type compatibility rules. The components directly declared in a record may only be of specialized class record and type..
type May only be predefined types, enumerations, array of type, or classes extending from type.
model Identical to class, the basic class concept, i.e., no restrictions and no enhancements.
block Same as model with the restriction that each connector component of a block must have prefixes input and/or output for all connector variables. [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 section 12.2 for restrictions and enhancements of functions. Enhanced to allow the function to contain an external function interface. [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 may 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 chapter 13 on packages.)
operator record Similar to record; but operator overloading is possible, and due to this the typing rules are different – see chapter 6. 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 [as short class definition, and not from another specialized class]. It is not legal to extend from any of its enclosing scopes. (See chapter 14).
operator Similar to package; but may only contain declarations of functions. May only be placed directly in an operator record. (See also chapter 14).
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.[
operator function foo ... end foo;
” is conceptually treated as
operator foo function foo1
  ...
end foo1; end foo;
]

[Example for ”operator”:

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; // not allowed, since extending from enclosing scope
  Real k;
end MyComplex;
operator record ComplexVoltage = Complex(re(unit="V"),im(unit="V")); // allowed

]

4.7 Balanced Models

[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:

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, but 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 section 9.3.

Definition 1: 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 [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 2: 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. This includes the proper count for when-clauses (see section 8.3.5), and algorithms (see section 11.1), and is also used for the flat Hybrid DAE formulation (see Appendix C).

  • The number of input and flow-variables present in each (top-level) public connector component. [This represents the number of connection equations that will be provided when the class is used.]

  • The number of (top level) public input variables that neither are connectors nor have binding equations [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.].

[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):

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 3: 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 [respecting 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.].

Definition 4: 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 [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. [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.]

  • 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:
    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 other cases (declaration of a component of a model or block type, modifiers on extends, and modifier on short-class-definitions): modifiers for components 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.

  • All non-partial model and block classes must be locally balanced [this means that the local number of unknowns equals the local equation size].

Based on these restrictions, the following strong guarantee can be given for simulation models and blocks:

Proposition 1:

All simulation models and blocks are globally balanced.
[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 1:

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:

0 = p.i+n.i;
u = p.v-n.v;
Cder(u) = p.i;

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 equationu = p.v - n.vwould 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 equationu = p.v - n.v” would be replaced by the equation “u = ” and the equation C*der(u) = p.i would be replaced by the equation “C*der(u) = ”, 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 2:

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 variable for c (c.p.i, c.n.i).

Local equations:

p.v = t.p.v;
0 = p.i-t.p.i;
c.p.v = load.n.v;
0 = c.p.i+load.n.i;
n.v = c.n.v;
0 = n.i-c.n.i;

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 non-singular model.]

Example 3:

import SI = Modelica.SIunits;
partial model BaseProperties
  "Interface of medium model for all type of media"
  parameter Boolean preferredMediumStates=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 use of connector here is a special design pattern. 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 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.

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:

model SimpleAir "Medium model of simple air. Independent variables: p,T"
  extends BaseProperties(nXi = 0,
     p(stateSelect = if preferredMediumStates then StateSelect.prefer
                       else StateSelect.default),
     T(stateSelect = if preferredMediumStates 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:

import SI = Modelica.SIunits;
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(preferredMediumStates=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.

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.
]

4.8 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. Attributes cannot be accessed using dot notation, and are not constrained by equations and algorithm sections. E.g. in Real x(unit=”kg”) = y; 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 are reserved such that it is illegal to declare an element with these names. [Thus it is possible to define a normal class called Clock in a package and extend from it.] The definitions use RealType, IntegerType, BooleanType, StringType, EnumType as mnemonics corresponding to machine representations. [Hence the only way to declare a subtype of e.g. Real is to use the extends mechanism.]

4.8.1 Real Type

The following is the predefined Real type:

type Real // Note: Defined with Modelica syntax although predefined
  RealType value; // Accessed without dot-notation
  parameter StringType quantity    = "";
  parameter StringType unit        = "" "Unit used in equations";
  parameter StringType displayUnit = "" "Default display unit";
  parameter RealType min=-Inf, max=+Inf; // Inf denotes a large value
  parameter RealType start    = 0; // Initial value
  parameter BooleanType fixed = true,  // default for parameter/constant;
                              = false; // default for other variables
  parameter RealType nominal;            // Nominal value
  parameter BooleanType unbounded=false; // For error control
  parameter StateSelect stateSelect = StateSelect.default;
equation
  assert(value >= min and value <= max, "Variable value out of limit");
end Real;

The nominal attribute is meant to be used for scaling purposes and to define tolerances in relative terms, see section 4.8.7.

4.8.2 Integer Type

The following is the predefined Integer type:

type Integer // Note: Defined with Modelica syntax although predefined
  IntegerType value; // Accessed without dot-notation
  parameter StringType quantity = "";
  parameter IntegerType min=-Inf, max=+Inf;
  parameter IntegerType start = 0; // Initial value
  parameter BooleanType fixed = true,  // default for parameter/constant;
                              = false; // default for other variables
equation
  assert(value >= min and value <= max, "Variable value out of limit");
end Integer;

4.8.3 Boolean Type

The following is the predefined Boolean type:

type Boolean // Note: Defined with Modelica syntax although predefined
  BooleanType value; // Accessed without dot-notation
  parameter StringType quantity = "";
  parameter BooleanType start = false; // Initial value
  parameter BooleanType fixed = true,  // default for parameter/constant;
                              = false, // default for other variables
end Boolean;

4.8.4 String Type

The following is the predefined String type:

type String // Note: Defined with Modelica syntax although predefined
  StringType value; // Accessed without dot-notation
  parameter StringType quantity = "";
  parameter StringType start = "";     // Initial value
  parameter BooleanType fixed = true,  // default for parameter/constant;
                              = false, // default for other variables
end String;

4.8.5 Enumeration Types

A declaration of the form

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:

type Size = enumeration(small, medium, large, xlarge);
Size t_shirt_size = Size.medium;

].

An optional comment string can be specified with each enumeration literal:

[Example:

type Size2 = enumeration(small "1st", medium "2nd", large "3rd", xlarge "4th");

]

An enumeration type is a simple type and the attributes are defined in section 4.8.5.1. 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 section 11.2.2.2. An element of an enumeration type can be accessed in an expression [e.g. an array index value].

[Example:

type DigitalCurrentChoices = enumeration(zero, one);
// Similar to Real, Integer

Setting attributes:

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:

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;

]

4.8.5.1 Attributes of Enumeration Types

For each enumeration:

type E=enumeration(e1, e2, ..., en);

a new simple type is conceptually defined as

type E // Note: Defined with Modelica syntax although predefined
  EnumType value; // Accessed without dot-notation
  parameter StringType quantity = "";
  parameter EnumType min=e1, max=en;
  parameter EnumType start = e1;       // Initial value
  parameter BooleanType fixed = true,  // default for parameter/constant;
                              = false; // default for other variables
  constant EnumType e1=...;
  ...
  constant EnumType en=...;
equation
  assert(value >= min and value <= max, "Variable value out of limit");
end E;

[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]

4.8.5.2 Type Conversion of Enumeration Values to String or Integer

The type conversion function Integer(<expression of enumeration type>) 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: String(E.Small) gives ”Small”.]

See also section 3.7.1.

4.8.5.3 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 section 4.8.5.2) enumeration value.

For an enumeration type named EnumTypeName, the expression EnumTypeName(<Integer expression>) 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:

type Colors = enumeration ( RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW );

Converting from Integer to Colors:

c = Colors(i);
c = Colors(10); // An error

]

4.8.5.4 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.

4.8.6 Clock Types

4.8.7 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. [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).] [For external functions in C89, RealType by default maps to double and IntegerType by default maps to int. In the mapping proposed in Annex F of the C99 standard, RealType/double matches the IEC 60559:1989 (ANSI/IEEE 754-1985) double format. Typically IntegerType represents a 32-bit 2-complement signed integer.]

4.8.8 Other Predefined Types

4.8.8.1 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.

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."
);

4.8.8.2 ExternalObject

See section 12.9.7 for information about the predefined type ExternalObject.

4.8.8.3 AssertionLevel

The predefined AssertionLevel enumeration type is used together with assert, section 8.3.7.

type AssertionLevel=enumeration(warning, error);

4.8.8.4 Connections

The package Connections is used for over-constrained connection graphs, section 8.3.9.

4.8.8.5 Graphical Annotation Types

A number of “predefined” record types and enumeration types for graphical annotations are described in chapter 18. These types are not predefined in the usual sense since they cannot be referenced in ordinary Modelica code, only within annotations.