Interface or Type Relationships¶
A class or component, e.g. denoted A, can in some cases be used at a location designed for another class or component, e.g. denoted B. In Modelica this is the case for replaceable classes (see ) and for inner/outer elements (see ). Replaceable classes are the primary mechanism to create very flexible models. In this chapter, the precise rules are defined when A can be used at a location designed for B. The restrictions are defined in terms of compatibility rules ( and ) between ”interfaces” (); this can also be viewed as sub-typing ().
In this chapter, two kinds of terminology is used for identical concepts to get better understanding [e.g. by both engineers and computer scientists]. A short summary of the terms is given in the following table. The details are defined in the rest of this chapter.
term | description | |
---|---|---|
type or interface | The “essential” part of the public declaration sections of a class that is needed to decide whether A can be used instead of B [E.g. a declaration “Real x” is part of the type, also called interface, but “import A” is not]. | |
inheritance interface | The “essential” part of the public and protected declaration sections of a class that is needed to decide whether A can be used instead of B. The class type , also called inheritance interface, is needed when inheritance takes place, since then the protected declarations have to be taken into account. | |
compatible interface | A is a subtype of B, or equivalently, the interface of A is compatible to the interface of B, if the “essential” part of the public declaration sections of B is also available in A [E.g., if B has a declaration “Real x”, this declaration must also be present in A. If A has a declaration “Real y”, this declaration must not be present in B]. | |
|
A is a restricted subtype of B, or equivalently, the interface of A is plug compatible to the interface of B, if A is a subtype of B and if connector components in A that are not in B, are default connectable. [E.g. it is not allowed that these connectors have variables with the “input” prefix, because then they must be connected.] A model or block A cannot be used instead of B, if the particular situation does not allow to make a connection to these additional connectors. In such a case the stricter “plug compatible” is required for a redeclaration. | |
function compatible interface | A is a function subtype of B, or equivalently, the interface of A is function compatible to the interface of B, if A is a subtype of B and if the additional arguments of function A that are not in function B are defined in such a way, that A can be called at places where B is called. [E.g. an additional argument must have a default value.] |
The Concepts of Type, Interface and Subtype¶
A type can conceptually be viewed as a set of values. When we say that the variable x has the type Real, we mean that the value of x belongs to the set of values represented by the type Real i.e., roughly the set of floating point numbers representable by Real, for the moment ignoring the fact that Real is also viewed as a class with certain attributes. Analogously, the variable b having Boolean type means that the value of b belongs to the set of values {false, true}. The built-in types Real, Integer, String, Boolean are considered to be distinct types.
The subtype relation between types is analogous to the subset relation between sets. A type A1 being a subtype of type A means that the set of values corresponding to type A1 is a subset of the set of values corresponding to type A.
The type Integer is not a subtype of Real in Modelica even though the set of primitive integer values is a subset of the primitive real values since there are some attributes of Real that are not part of Integer ().
The concept of interface as defined in and used in this document is equivalent to the notion of type based on sets in the following sense:
An element is characterized by its interface defined by some attributes (). The type of the element is the set of values having the same interface, i.e. the same attributes.
A subtype A1 in relation to another type A, means that the elements of the set corresponding to A1 is a subset of the set corresponding to A, characterized by the elements of that subset having additional properties.
[Example:
A record R: record R Boolean b; Real x; end R;
Another record called R2: R2 Boolean b; Real x; Real y; end R2;
An instance r: R r;
An instance r2: R2 r2;
The type R of r can be viewed as the set of all record values having the attributes defined by the interface of R, e.g. the infinite set {R(b=false,x=1.2), R(b=false, x=3.4), R(b=true, x=1.2), R(b=true, x=1.2, y=2), R(b=true, x=1.2, a=2),…). The statement that r has the type (or interface) R means that the value of r belongs to this infinite set.
The type R2 is a subtype of R since its instances fulfill the additional property of having the component Real y; in all its values.]
]
Interface or Type¶
Based on a flattened class or component we can construct an interface for that flattened class or component. The interface or type [the terms interface and type are equivalent and can be used interchangeably] is defined as the following information about the flattened element itself:
Whether it is replaceable or not.
Whether the class itself or the class of the component is transitively non-replaceable (), and if not, the reference to the replaceable class it refers to.
Whether it is a component or a class.
Additional information about the element:
- The flow- or stream-prefix.
- Declared variability (constant, parameter, discrete).
- The prefixes input and output.
- The prefixes inner and/or outer.
- Whether the declaration is final, and in that case its semantics contents.
- Array sizes (if any).
- Condition of conditional components (if any).
- Which kind of specialized class.
- For an enumeration type or component of enumeration type the names of the enumeration literals in order.
- Whether it is a built-in type and the built-in type (RealType, IntegerType, StringType or BooleanType).
- Only for an operator record class and classes derived from ExternalObject: the full name of the operator record base-class (i.e. the one containing the operations), or the derived class. See and .The following item does not apply for an operator record class or class derived from ExternalObject, since the type is already uniquely defined by the full name.
For each named public element of the class or component (including both local and inherited named elements) a tuple comprised of:
- Name of the element.
- Interface or type of the element. This might have been modified by modifiers and is thus not necessarily identical to the interface of the original declaration.
The corresponding constraining interface is constructed based on the constraining type () of the declaration (if replaceable – otherwise same as actual type) and with the constraining interface for the named elements.
In a class all references to elements of that class should be limited to their constraining interface (i.e. only public elements and if the declaration is replaceable limited to the constraining interface).
[The public interface does not contain all of the information about the class or component. When using a class as a base-class we also need protected elements, and for internal type-checking we need e.g. import-elements. However, the information is sufficient for checking compatibility and for using the class to flatten components.]
Transitively non-Replaceable¶
[In several cases it is important that no new elements can be added to the interface of a class, especially considering short class definitions. Such classes are defined as transitively non-replaceable.]
A class reference is transitively non-replaceable iff (i.e. “if and only if”) all parts of the name satisfy the following:
- If the class definition is long it is transitively non-replaceable if not declared replaceable.
- If the class definition is short (i.e. ‘class A=P.B’) it is transitively non-replaceable if it is non-replaceable and equal to class reference (“P.B”) that is transitively non-replaceable.
[According to , for a hierarchical name all parts of the name must be transitively non-replaceable, i.e. in “extends A.B.C” this implies that A.B.C must be transitively non-replaceable, as well as A and A.B, with the exception of the “class extends redeclaration mechanism” see ]
Inheritance Interface or Class Type¶
For inheritance the interface also must include protected elements; this is the only change compared to above.
Based on a flattened class we can construct an inheritance interface or class type for that flattened class. The inheritance interface or class type is defined as the following information about the flattened element itself:
- Whether it is replaceable or not.
- Whether the class itself or the class of the component is transitively non-replaceable (), and if not the reference to replaceable class it refers to.
- For each named element of the class (including both local and
inherited named elements) a tuple comprised of:
- Name of the element.
- Whether the element is component or a class.
- For elements that are classes: Inheritance interface or class type of the element. This might have been modified by modifiers and is thus not necessarily identical to the interface of the original declaration.
- For elements that are components: interface or type of the element. This might have been modified by modifiers and is thus not necessarily identical to the interface of the original declaration.
- Additional information about the element:
- The flow- or stream-prefix.
- Declared variability (constant, parameter, discrete).
- The prefixes input and output.
- The prefixes inner and/or outer.
- Whether the declaration is final, and in that case its semantics contents.
- Array sizes (if any).
- Condition of conditional components (if any).
- Which kind of specialized class.
- For an enumeration type or component of enumeration type the names of the enumeration literals in order.
- Whether it is a built-in type and the built-in type (RealType, IntegerType, StringType or BooleanType).
- Visibility (public or protected).
Interface Compatibility or Subtyping¶
An interface of a class or component A is compatible with an interface of a class or component B (or the constraining interface of B), or equivalently that the type of A is a subtype of the type of B, iff [intuitively all important elements of B must be present in A ]:
- A is a class if and only if B is a class (and thus: A is a component if and only if B is a component).
- If A has an operator record base-class then B must also have one and it must be the same. If A does not have an operator record base-class then B may not have one. See .
- If A is derived from ExternalObject, then B must also be derived from ExternalObject and have the same full name. If A is not derived from ExternalObject then B may not derived from ExternalObject. See .
- If B is not replaceable then A may not be replaceable.
- If B is transitively non-replaceable then A must be transitively non-replaceable (). For all elements of the inheritance interface of B there must exist a compatible element with the same name and visibility in the inheritance interface of A. The interface of A may not contain any other elements. [We might even extend this to say that A and B should have the same contents, as in the additional restrictions below.]
- If B is replaceable then for all elements of the component interface of B there must exist a plug-compatible element with the same name in the component interface of A.
- If B is neither transitively non-replaceable nor replaceable then A must be linked to the same class, and for all elements of the component interface of B there must thus exist a plug-compatible element with the same name in the component interface of A.
- Additional restrictions on the additional information. These elements
should either match or have a natural total order:
- If B is a non-replaceable long class definition A must also be a long class definition.
- The flow-or stream-prefix should be matched for compatibility.
- Variability is ordered constant< parameter< discrete< (no prefix: continuous-time for Real), and A is only compatible with B if the declared variability in A is less than or equal the variability in B. For a redeclaration of an element the variability prefix is as default inherited by the redeclaration (i.e. no need to repeat ‘parameter’ when redeclaring a parameter).
- The input and output prefixes must be matched. This ensures that the rules regarding inputs/outputs for matching connectors and (non-connector inputs) are preserved, as well as the restriction on blocks. For a redeclaration of an element the input or output prefix is inherited from the original declaration.
- The inner and/or outer prefixes should be matched. For a redeclaration of an element the inner and/or outer prefixes are inherited from the original declaration (since it is not possible to have inner and/or outer as part of a redeclare).
- If B is final A must also be final and have the same semantic contents.
- The number of array dimensions in A and B must be matched.
- Conditional components are only compatible with conditional components. The conditions must have equivalent contents (similar as array sizes – except there is no “:” for conditional components). For a redeclaration of an element the conditional part is inherited from the original.
- A function class is only compatible with a function class, a package class only compatible with a package class, a connector class only with a connector class, a model or block class only compatible with a model or block class, and a type or record class only compatible with a type or record class.
- If B is an enumeration type A must also be an enumeration type and vice versa. If B is an enumeration type not defined as (:) then A must have the same enumeration literals in the same order; if B is an enumeration type defined as (:) then there is no restriction on the enumeration type A.
- If B is a built-in type then A must also be of the same built-in type and vice versa.
Plug-compatibility is a further restriction of compatibility (subtyping) defined in , and further restricted for functions, see . For a replaceable declaration or modifier the default class must be compatible with the constraining class.
For a modifier the following must apply:
- The modified element should exist in the element being modified.
- The modifier should be compatible with the element being modified, and in most cases also plug-compatible, .
[If the original constraining flat class is legal (no references to unknown elements and no illegal use of class/component), and modifiers legal as above – then the resulting flat class will be legal (no references to unknown elements and no illegal use of class/component and compatible with original constraining class) and references refer to similar entities.]
Plug-Compatibility or Restricted Subtyping¶
[If a sub-component is redeclared, see , it is impossible to connect to any new connector. A connector with input prefix must be connected to, and since one cannot connect across hierarchies, one should not be allowed to introduce such a connector at a level where a connection is not possible. Therefore all public components present in the interface A that are not present in B must be connected by default.]
Definition 5: Plug-compatibility (= restricted subtyping)
An interface A is plug-compatible with (a restricted subtype of) an interface B (or the constraining interface of B) iff:
- A is compatible with (subtype of) B.
- All public components present in A but not in B must be default-connectable (as defined below).
Definition 6: Default connectable
A component of an interface is default-connectable iff:
- All of its components are default connectable.
- A connector component must not be an input. [Otherwise a connection to the input will be missing.]
- A connector component must not be of an expandable connector class. [The expandable connector does potentially have inputs.]
- A parameter, constant, or non-connector input must either have a binding equation or all of its sub-components must have binding equations.
Based on the above definitions, there are the following restrictions:
- A redeclaration of an inherited top-level component must be compatible with (subtype of) the constraining interface of the element being redeclared.
- In all other cases redeclarations must be plug-compatible with the constraining interface of the element being redeclared.
[The reason for the difference is that for an inherited top-level component it is possible to connect to the additional connectors, either in this class or in a derived class.
Example:
partial model TwoFlanges
Modelica.Mechanics.Rotational.Interfaces.Flange_a flange_a;
Modelica.Mechanics.Rotational.Interfaces.Flange_b flange_b;
end TwoFlanges;
partial model FrictionElement
extends TwoFlanges;
...
end FrictionElement;
model Clutch "compatible - but not plug-compatible with FrictionElement"
Modelica.Blocks.Interfaces.RealInput pressure;
extends FrictionElement;
...
end Clutch;
model DriveLineBase
extends TwoFlanges;
Inertia J1;
replaceable FrictionElement friction;
equation
connect(flange_a, J1.flange_a);
connect(J1.flange_b, friction.flange_a);
connect(friction.flange_b, flange_b);
end DriveLineBase;
model DriveLine
extends DriveLineBase(redeclare Clutch friction);
Constant const;
equation
connect(const.y, frition.pressure);
// Legal connection to new input connector.
end DriveLine;
model UseDriveLine "illegal model"
DriveLineBase base(redeclare Clutch friction);
// Cannot connect to friction.pressure
end UseDriveLine;
If a subcomponent is redeclared, it is impossible to connect to any new connector. Thus any new connectors must work without being connected, i.e., the default connection of flow-variables. That fails for inputs (and expandable connectors may contain inputs). For parameters and non-connector inputs it would be possible to provide bindings in a derived class, but that would require hierarchical modifiers and it would be bad modeling practice that a hierarchical modifier must be used in order to make a model valid. A replaceable class might be used as the class for a sub-component, therefore plug-compatibility is required not only for replaceable sub-components, but also for replaceable classes.]
Function-Compatibility or Function-Subtyping for Functions¶
[Functions may be called with either named or positional arguments, and thus both the name and order is significant. If a function is redeclared, see , any new arguments must have defaults (and be at the end) in order to preserve the meaning of existing calls.]
Definition 7: Function-Compatibility or Function-Subtyping for Functions
A function class A is function-compatible with or a function subtype of function class B iff, [The terms function-compatible and function subtype of are synonyms and used interchangeably]:
- A is compatible to (subtype of) B.
- All public input components of B have correspondingly named public input components of A in the same order and preceding any additional public input components of A.
- All public output components of B have correspondingly named public output components of A in the same order and preceding any additional public output components of A.
- A public input component of A must have a binding assignment if the corresponding named element has a binding assignment in B.
- A public input component of A not present in B must have a binding assignment.
Based on the above definition the following restriction holds:
- The interface of a redeclared function must be function-compatible with or a function subtype of the constraining interface of the function being redeclared.
[Example: Demonstrating a redeclaration using a function-compatible function
function GravityInterface
input Modelica.SIunits.Position position[3];
output Modelica.SIunits.Acceleration acceleration[3];
end GravityInterface;
function PointMassGravity
extends GravityInterface;
input Modelica.SIunits.Mass m;
algorithm
acceleration := -Modelica.Constants.G*m*position/(position*position)^1.5;
end PointMassGravity;
model Body
model UseDriveLine "illegal model"
DriveLineBase base(redeclare Clutch friction);
// Cannot connect to friction.pressure
end UseDriveLine;
Modelica.Mechanics.MultiBody.Interface.Frame_a frame_a;
replaceable function gravity = GravityInterface;
equation
frame_a.f = gravity(frame_a.r0);
// or gravity(position=frame_a.r0);
frame_a.t = zeros(3);
end Body;
model PlanetSimulation
function sunGravity = PointMassGravity (m=2e30);
Body planet1(redeclare function gravity = sunGravity);
Body planet2(redeclare function gravity = PointMassGravity (m=2e30));
...
end PlanetSimulation;
Note: PointMassGravity is not function-compatible with GravityInterface (no default for m), but sunGravity inside PlanetSimulation is function-compatible with GravityInterface.]
Type Compatible Expressions¶
Certain expressions consist of an operator applied to two or more type compatible sub-expressions (A and B), including binary operators, e.g. A + B, if-expressions, e.g. if x then A else B, and array expressions, e.g. {A,B}. The resulting type of the expression in case of two type compatible subexpressions A and B is defined as follows:
- If A is a record-expression B must also be a record-expression with the same named elements. The type compatible expression is a record comprised of named elements that are compatible with the corresponding named elements of both A and B.
- If A is an array expression then B must also be an array expression, and ndims(A)=ndims(B). The type compatible expression is an array expression with elements compatible with the elements of both A and B. If both size(A) and size(B) are known and size(A)=size(B) then this defines the size of the type compatible expression, otherwise the size of the expression is not known until the expression is about to be evaluated. In case of an if-expression the size of the type compatible expression is defined based on the branch selected, and for other cases size(A)=size(B) must hold at this point.
- If A is a scalar expression of a simple type B must also be a scalar expression of a simple type.
- If A is a Real expression then B must be a Real or Integer expression and the type compatible expression is Real.
- If A is an Integer expression then B must be a Real or Integer expression. For exponentiation and division the type compatible expression is Real (even if both A and B are Integer) see 10.610.6, in other cases the type compatible expression is Real or Integer (same as B).
- If A is a Boolean expression then B must be a Boolean expression and the type compatible expression is Boolean.
- If A is a String expression then B must be a String expression and the type compatible expression is String.
- If A is an enumeration expression then B must be an enumeration expression and the type compatible expression is enumeration expression, and all enumeration expressions must be defined in terms of an enumeration type with the same enumeration literals in the same order.
- If A has an operator record base-class then B must also have an operator record base-class, and it must be the same, and otherwise neither A nor B may have an operator record base-class. This is also the operator record base-class for the expression e.g. for ‘if (cond) then A else B’.
- If A is derived from ExternalObject then B must also be derived from ExternalObject and they must have the same full name; and otherwise neither A nor B may be derived from ExternalObject. The common full name also defines the type of the expression, e.g. for ‘if (cond) then A else B’.