Modelica® Language Specification version 3.6

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

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

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.

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.

4.4.1 Syntax

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

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 scalar non-negative parameter expressions of type Integer, a reference to a type (which must an enumeration type or Boolean, see section 4.8.5), or the colon operator denoting that the array dimension is left unspecified (see section 10.1). All variants can also be part of short class definitions.

[Example: Variables with array dimensions:

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 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. These are a subset of the binding equations, see section 8.1. 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 section 3.8). 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, section 11.2.1.2.

4.4.2.2 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, section 4.6. 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 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.

Variables declared with the stream type prefix shall be a subtype of Real. This is further restricted in section 15.1.

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 definition 9.1), 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 or an operator record defining an additive group, see section 9.2.

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 section 4.4.4.1 for structured components.

[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 cannot be applied to structured components. The type prefix flow can be applied to structured components, see section 9.2. 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: 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 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.

[There is no exception for parameters with fixed = false, despite the fact that such parameters are generally allowed to be initialized from systems of dependent equations. However, a parameter with fixed = false can use an initial equation instead of a binding equation, allowing for 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

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 during simulation:

  • A variable vc declared with constant prefix remains constant during simulation, with a value that is unaffected even by the initialization problem. This is called a constant, or constant variable.

  • A variable vc declared with the parameter prefix has a value determined at initialization, and keeps that value during the entire simulation. Thus it is known and non-changing during transient analysis. This is called a parameter, or parameter variable.

  • 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. It 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. During transient analysis the variable can only change its value at event instants (see section 8.5).

  • A continuous-time variable is a Real variable without any prefix that is not assigned in a when-clause. A continuous-time variable 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 section 8.5). It may also contain a combination of these effects. Regarding existence of der(vn), see operator 3.10.

Components declared as constant 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.

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

[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 section 3.8.]

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

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

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:

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

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

[A parameter 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 section 6.4).

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. A component declared with a condition-attribute can only be modified and/or used in connections.

[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 section 18.7.]

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. 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 (section 6.3).

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

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.

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 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-clause ] ";" ]
  [ annotation-clause ";" ]

4.5.1 Short Class Definitions

A class definition of the form

class IDENT1 = type-specifier class-modification;

is identical, except that type-specifier (the base-class) 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 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: 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;
  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 5.3.2), due to the encapsulated restriction.]

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: The types of f1 and f2 are identical:

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:

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 Combining Base Classes and Other Elements

It is not legal to combine equations, algorithms, components, base classes, 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.

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

[Example: 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 definitions of the specialized classes are given below (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 shall not have prefixes input, output, inner, outer, stream, or flow. Enhanced with implicitly available record constructor function, see section 12.6. 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.

    [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 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 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. It is not legal to extend from any of its enclosing scopes. (See chapter 14).

  • operator – 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.

    [A function declaration

    operator function foo  end foo;

    is conceptually treated as

    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: Use of 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; // Error; 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. 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 4.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 4.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 B).]

  • 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, due to the balancing restrictions for connectors, see section 9.3.1.]

  • 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 4.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. ∎

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

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

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

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

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

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

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;
C𝑑𝑒𝑟(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 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:

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:

p.v =t.p.v;
0 =p.i-t.p.i;
c.p.v =t.n.v;
0 =c.p.i+t.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 singular model.]

[Example: Example 3:

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 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 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+𝚗𝚇𝚒 (p, h, Xi: variables inside connectors defined in BaseProperties and inherited in SimpleAir)

resulting in 5+𝚗𝚇𝚒 unknowns. The local equation size is:

  • 3 (equations defined in SimpleAir), plus

  • 2+𝚗𝚇𝚒 (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 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𝚗𝚇𝚒 (inside the port connector), plus

  • 2+𝚗𝚇𝚒 (variables U, M and MXi), plus

  • 2+𝚗𝚇𝚒 (the input variables in the connectors of the medium model)

resulting in 8+4𝚗𝚇𝚒 unknowns; the local equation size is

  • 6+3𝚗𝚇𝚒 from the equation section, plus

  • 2+𝚗𝚇𝚒 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𝚗𝚇𝚒 (inside the port connector), plus

  • 2+𝚗𝚇𝚒 (the input variables in the connectors of the medium model)

resulting in 6+3𝚗𝚇𝚒 unknowns, while the local equation size is

  • 4+2𝚗𝚇𝚒 from the equation section, plus

  • 2+𝚗𝚇𝚒 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. 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 value in the definitions of the predefined types represents the value of an expresion of that type. Unlike attributes, the value of a component cannot be referred to by name; both access and modification of the value is made directly on the component.

[Example: Accessing and modifying a variable value, using Real as example of a predefined type:

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

[Hence, it is possible to define a normal class called Clock in a package and extend from it.]

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

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

4.8.1 Real Type

The following is the predefined Real type:

type Real // Note: Defined with Modelica syntax although predefined
  RealType value; // 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 = -Inf, max = +Inf; // Inf denotes a large value
  parameter RealType start;            // 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(min <= value 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.6.

The fallback value is the closest value to 0.0 consistent with the min and max bounds.

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

4.8.2 Integer Type

The following is the predefined Integer type:

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

The minimal recommended number range for IntegerType is from -2147483648 to +2147483647, corresponding to a two’s-complement 32-bit integer implementation.

The fallback value is the closest value to 0 consistent with the min and max bounds.

4.8.3 Boolean Type

The following is the predefined Boolean type:

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

The fallback value is false.

4.8.4 String Type

The following is the predefined String type:

type String // Note: Defined with Modelica syntax although predefined
  StringType value; // Not an attribute; only 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;

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

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.

[Uses of elements of enumeration type in expressions include indexing into an array.]

[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; // 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 <= value and value <= max, "Variable value out of limit");
end E;

The fallback value is the min bound.

[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 Conversion of Enumeration 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 𝙸𝚗𝚝𝚎𝚐𝚎𝚛(𝙴.𝚎𝟷)=1, 𝙸𝚗𝚝𝚎𝚐𝚎𝚛(𝙴.𝚎𝚗)=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 Conversion of Integer to Enumeration

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

4.8.7 Other Predefined Types

4.8.7.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.7.2 ExternalObject

See section 12.9.7 for information about the predefined type ExternalObject.

4.8.7.3 AssertionLevel

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

type AssertionLevel = enumeration(warning, error);

4.8.7.4 Connections

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

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

4.8.7.6 Clock Types