Porting Modelica libraries to OpenModelica ========================================== One of the goals of OpenModelica is to provide a full, no-compromise implementation of the latest version of the `Modelica Language Specification `_, released by the non-profit `Modelica Association `_. This means that a main requirement for a Modelica library to work in OpenModelica is to be fully compliant to the Language Specification. Libraries and models developed with other Modelica tools may contain some code which is not valid according to the current language specification, but still accepted by that tool, e.g. to support legacy code of their customers. In order to use those libraries and models in OpenModelica, one needs to make sure that such code is replaced by a valid one. Note that getting rid of invalid Modelica code does not make the library *only* usable in OpenModelica; to the contrary, doing that is the best guarantee that the library will be usable *both* with the original tool used for development *and* with OpenModelica, as well as with any other present or future Modelica tool that follows the standard strictly. The first recommendation is to use any flag or option of the tool that was originally used to develop the library, that allows to check for strict compliance to the language specification. For example, Dymola features a translation option 'Pedantic mode for checking Modelica semantics' that issues an error if non-standard constructs are used. For your convenience, here you can find a list of commonly reported issues. Mapping of the library on the file system ----------------------------------------- Packages can be mapped onto individual *.mo* files or onto hierarchical directory structures on the file system, according to the rules set forth in `Section 13.4 `_. of the language specification. The file encoding must be UTF-8; the use of a BOM at the beginning of the file is deprecated and preferably avoided. If there are non-ASCII characters in the comments or in the documentation of your library, make sure that the file is encoded as UTF-8. If a directory-based representation is chosen, each *.mo* file must start with a *within* clause, and each directory should contain a *package.order* file that lists all the classes and constants defined as separate files in that directory. When using revision control systems such as GIT or SVN, if the library is stored in a directory structure, it is recommended to include the top-level directory (that must have the same name as the top-level package) in the repository itself, to avoid problems in case the repository is cloned locally on a directory that doesn't have the right name. The top-level directory name, or the single *.mo* file containing the entire package, should be named exactly as the package (e.g. *Modelica*), possibly followed by a space and by the version number (e.g. *Modelica 3.2.3*). Modifiers for arrays -------------------- According to the rules set forth in `Section 7.2.5 `_ of the language specification, when instantiating arrays of components, modifier values should be arrays of the same size of the component array, unless the *each* prefix is introduced, in which case the scalar modifier values is applied to all the elements of the array. Thus, if *MyComponent* has a Real parameter *p*, these are all valid declarations .. code-block:: modelica parameter Real q = {0, 1, 2}; MyComponent ma[3](p = {10, 20, 30}); MyComponent mb[3](p = q); MyComponent mb[3](each p = 10); while these are not .. code-block:: modelica parameter Real r = 4; MyComponent ma[3](p = r); MyComponent mb[3](p = 20); In most cases, the problem is solved by simply adding the *each* keyword where appropriate. Access to conditional components -------------------------------- According to `Section 4.4.5 `_ of the language specification, "A component declared with a condition-attribute can only be modified and/or used in connections". When dealing, e.g., with conditional input connectors, one can use the following patterns: .. code-block:: modelica model M parameter Boolean activateIn1 = true; parameter Boolean activateIn2 = true; Modelica.Blocks.Interfaces.RealInput u1_in if activateIn1; Modelica.Blocks.Interfaces.RealInput u2_in = u2 if activateIn2; Real u2 "internal variable corresponding to u2_in"; Real y; protected Modelica.Blocks.Interfaces.RealInput u1 "internal connector corresponding to u1_in"; equation y = u1 + u2; connect(u1_in, u1) "automatically disabled if u1_in is deactivated"; if not activateIn1 then u1 = 0 "default value for protected connector value when u1_in is disabled"; end if; if not activateIn2 then u2 = 0 "default value for u2 when u2_in is disabled"; end if; end M; where conditional components are only used in connect equations. The following patterns instead are not legal: .. code-block:: modelica model M parameter Boolean activateIn1 = true; parameter Boolean activateIn2 = true; Modelica.Blocks.Interfaces.RealInput u1_in if activateIn1; Modelica.Blocks.Interfaces.RealInput u2_in if activateIn2; Real u1 "internal variable corresponding to u1_in"; Real u2 "internal variable corresponding to u2_in"; Real y; equation if activateIn1 then u1 = u1_in "invalid: uses conditional u1_in outside connect equations"; end if; if activateIn2 then u2 = u2_in "invalid: uses conditional u1_in outside connect equations"; end if; y = u1 + u2; end M; because those components are also used in other equations. The fact that those equations are conditional and are not activated when the corresponding conditional components are also not activated is irrelevant, according to the language specification. Access to classes defined in partial packages --------------------------------------------- Consider the following example package .. code-block:: modelica package TestPartialPackage partial package PartialPackage function f input Real x; output Real y; algorithm y := 2*x; end f; end PartialPackage; package RegularPackage extends PartialPackage; model A Real x = time; end A; end RegularPackage; model M1 package P = PartialPackage; Real x = P.f(time); end M1; model M2 extends M1(redeclare package P = RegularPackage); end M2; model M3 encapsulated package LocalPackage import TestPartialPackage.PartialPackage; extends PartialPackage; end LocalPackage; package P = LocalPackage; Real x = P.f(time); end M3; end TestPartialPackage; Model *M1* references a class (a function, in this case) from a partial package. This is perfectly fine if one wants to write a generic model, which is then specialized by redeclaring the package to a non-partial one, as in *M2*. However, *M1* cannot be compiled for simulation, since, according to `Section 5.3.2 `_ of the language specification, the classes that are looked inside during lookup shall not be partial in a simulation model. This problem can be fixed by accessing that class (the function *f*, in this case) from a non-final package that extends the partial one, either by redeclaring the partial package to a non-partial one, as in *M2*, or by locally defining a non-partial package that extends from the partial one, as in *M3*. The latter option is of course viable only if the class being accessed is in itself not a partial or somehow incomplete one. This issue is often encountered in models using *Modelica.Media*, that sometimes use some class definitions (e.g. unit types) from partial packages such as *Modelica.Media.Interfaces.PartialMedium*. The fix in most cases is just to use the same definition from the actual replaceable *Medium* package defined in the model, which will eventually be redeclared to a non-partial one in the simulation model. Equality operator in algorithms ------------------------------- The following code is illegal, because it uses the equality '=' operator, which is reserved for equations, instead of the assignment operator ':=' inside an algorithm: .. code-block:: modelica function f input Real x; input Real y = 0; output Real z; algorithm z = x + y; end f; so, the OpenModelica parser does not accept it. The correct code is: .. code-block:: modelica function f input Real x; input Real y = 0; output Real z; algorithm z := x + y; end f; Some tools automatically and silently apply the correction to the code, please save it in its correct form to make it usable with OpenModelica. Also note that binding *equations* with '=' sign are instead required for default values of function inputs. Public non-input non-output variables in functions -------------------------------------------------- According to `Section 12.2 `_ of the language specification, only input and output formal parameters are allowed in the function's public variable section. Hence, the following function declaration is not valid: .. code-block:: modelica function f input Real x; output Real y; Real z; algorithm z := 2; y := x+z; end f; and should be fixed by putting the variable *z* in the protected section: .. code-block:: modelica function f input Real x; output Real y; protected Real z; algorithm z := 2; y := x+z; end f; Subscripting of expressions --------------------------- Some libraries use expression subscripting, e.g. .. code-block:: modelica model M Real x[3]; Real y[3]; Real z; equation z = (x.*y)[2]; ... end M; This construct is already accepted by some Modelica tools, but is not yet included in the current Modelica Specification 3.6, so it is not supported in OpenModelica up to version 1.22.0. It has now been included in the draft for the 3.7 language specification, so it will be implemented in the future also by OpenModelica. Incomplete specification of initial conditions ---------------------------------------------- The simulation of Modelica models of dynamical systems requires the tool to determine a consistent initial solution for the simulation to start. To do so, the system equations are augmented by adding one initial condition for each continuous state variable (after index reduction) and one initial condition for each discrete state variable. Then, the augmented system is solved upon initialization. These initial conditions can be formulated by adding a *start = * and a *fixed = true* attribute to those variables, e.g. .. code-block:: modelica parameter Real x_start = 10; parameter Real v_start = 2.5; Real x(start = x_start, fixed = true); discrete Real v(start = v_start, fixed = true); Integer i(start = 2, fixed = true); or by adding initial equations, e.g.: .. code-block:: modelica parameter Real x_start = 10; parameter Real v_start = 2.5; Real x; discrete Real v; Integer i; Real y(start = 3.5); initial equation x = x_start; v = v_start; i = 2; der(y) = 0; Note that in the latter case, the start attribute on *y* is not used directly to set the initial value of that variable, but only potentially used as initial guess for the solution of the initialization problem, that may require using an iterative nonlinear solver. Also note that sets of initial equations are often added to the models taken from reusable component libraries by selecting certain component parameters, such as *initOpt* or similar. If the number of initial conditions matches the number of continuous and discrete states, then the initialization problem is well-defined. Although this is per se not a guarantee that all tools will be able to solve it and find the same solution, this is for sure a prerequisite for across-tool portability. Conversely, if the number of initial conditions is less than the number of states, the tool has to add some initial equations, using some heuristics to change the fixed attribute of some variables from false to true. Consider for example the following model: .. code-block:: modelica model M Real x; Real y(start = 1); Real z(start = 2); equation der(x) = y + z; y = 2*x; z = 10*x + 1; end M; This model has one state variable *x*, no variables with *fixed = true* attributes and no initial equation, so there is one missing initial condition. One tool could choose to add the *fixed = true* attribute to the state variable *x*, fixing it to the default value of zero of its *start* attribute. Or, it could decide to give more priority to variables that have an explicitly modified *start* attribute, hence fix the initial value of *y* to 1, or the initial value of *z* to 2. Three completely different simulations would ensue. The Modelica Language Specification, `Section 8.6 `_ does not prescribe or recommend any specific choice criterion in this case. Hence, different tools, or even different versions of the same tool, could add different initial conditions, leading to completely different simulations. In order to avoid any ambiguity and achieve good portability, it is thus recommended to make sure that the initial conditions of all simulation model are well-specified. A model with not enough initial conditions causes the OMC to issue the following translation warning: "The initial conditions are not fully specified". By activating the Tools | Options | Simulation | Show additional information from the initialization process option, or the *-d=initialization* compiler flag, one can get an explicit list of the additional equations that OpenModelica automatically adds to get a fully specified initialization problem, which may be helpful to figure out which initial conditions are missing. In this case, we recommend to amend the source code of the model by adding suitable extra initial conditions, until that warning message no longer appears. Modelica_LinearSystems2 Library ------------------------------- The Modelica_LinearSystem2 library was originally developed in Dymola with a plan of eventually making it part of the Modelica Standard Library (thus the underscore in the library name). The library is based on several functions, e.g. *readStringMatrix()*, *simulateModel()*, *linearizeModel()* that are built-in Dymola functions but are not part of the Modelica Standard Library. In principle, these functions could be standardized and become part of the ModelicaServices library, which collects standardized interfaces to tool-specific functionality; then, OpenModelica could easily implement them based on its internal functionality. However, until this effort is undertaken, the Modelica_LinearSystem2 library cannot be considered as a full-fledged Modelica library, but only a Dymola-specific one. If you are interested in using this library in OpenModelica and are willing to contribute to get it supported, please contact the development team, e.g. by opening an ticket on the issue tracker.