diff --git a/RationaleMCP/0027/ReadMe.md b/RationaleMCP/0027/ReadMe.md index 6d604d515..59ca8d831 100644 --- a/RationaleMCP/0027/ReadMe.md +++ b/RationaleMCP/0027/ReadMe.md @@ -1,5 +1,5 @@ # Modelica Change Proposal MCP-0027
Units of Literal Constants -Francesco Casella, Henrik Tidefelt +Francesco Casella, Henrik Tidefelt, Hans Olsson **(In Development)** @@ -11,24 +11,45 @@ The problem with undefined unit is that it gets in the way of carrying out check | Date | Description | | --- | --- | | 2022-10-04 | Henrik Tidefelt. Filling this document with initial content. | +| 2025-12-18 | Hans Olsson, simple proposal https://github.com/modelica/ModelicaSpecification/issues/2127#issuecomment-349162852 | +| 2026-01-07 | Hans Olsson, improved - based on feedback | ## Contributor License Agreement All authors of this MCP or their organizations have signed the "Modelica Contributor License Agreement". ## Rationale -FIXME +The basic rationale for using units is to reduce the risk of errors. + +For the specific rules the rationale is that treating literals as having unit `"1"` in multiplicative contexts will catch many simple errors, without requriring excessive changes. +Thus e.g., `SI.Temperature T=293.15` and `SI.Enthalphy h=Medium.h_pT(1e5, 298.15)` are allowed. +In order to handle connectors in a good way, `zeros(n)` is treated the same as literals, allowing `a.f+b.f=zeros(3);` and `a.f=f0*zeros(3);`, but `a.f=T0*zeros(3);` (when `a.f` is a force and `T0` a temperature) is detected as an error. + +The rationale for only giving the rules for variables instead of providing a specific implementation is to make the expectation clear for library authors. +And at the same time allow tool vendors to implement the rules to varying degrees (there is sufficient experience with the prototypes to ensure that it will work). + +The rationale for considering implementing the rules to varying degrees, and even considering rules beyond the proposed ones is to ensure that they are consistent. +That means that rules are designed such that a model unit-consistent with the most restrictive rules will also be unit-consistent with less restrictive rules with a consistent subset of inferred units. + +Many libraries, including the Modelica Standard Library already largely follow this rule. ## Backwards Compatibility As current Modelica doesn't clearly reject some models with non-sensical combination of units, this MCP will break backwards compatibility by turning at least some of these invalid. +It is thus necessary to have the possibility to disable the rules for specific libraries (and specific equations in other libraries) to ease the adoption. + ## Tool Implementation -None, so far. +For scalars implemented in some version of Wolfram System Modeler (to be given). +Almost fully implemented on a flag in Dymola 2026x, and 3D Experience platform. ### Experience with Prototype -N/A +Generally it correctly finds some issues in libraries, but some libraries have systematic issues. +E.g., the buildings library uses a large number of multiplicative literals without unit for converting between different time and power units. ## Required Patents To the best of our knowledge, there are no patents that would conflict with the incorporation of this MCP. -## References -(None.) +## Details + +[Design details](design.md) + + diff --git a/RationaleMCP/0027/design.md b/RationaleMCP/0027/design.md new file mode 100644 index 000000000..0d552e89e --- /dev/null +++ b/RationaleMCP/0027/design.md @@ -0,0 +1,130 @@ +## References +https://doi.org/10.3384/ecp21817 and its references. + +## Detailed rules + +Unit restrictions for variables +- Each scalar variable and array element may only have one unit during the simulation. +- Arrays may have heterogenous units. Notes: + - This is for each instance of the variable, so different component instances and function calls (of the same model/function) may have different units. + - This applies for each value of the [evaluable parameters](#evaluable-parameter). + - The s-parametrization needed for diodes and friction requires special work-arounds in models. + +General rules + +- Expressions (including equations, binding equations, and start values) must be unit consistent, except for listed exceptions, and can be used to infer units. +- If a variable has a non-empty unit-attribute that is the unit of the variable. The unit-attributes should preferable be in base SI-units. +- Variables that are declared without unit-attribute (or with empty one) have unspecified unit, which may be inferred if there is a unique unit that makes the expressions unit consistent. + +Detailed default rules: + +- Literals without unit and zeros() are treated as empty unit except in multiplicative context (multiplication and division operators) where it has multiplicative-unit with the following rules: + - If both operands have multiplicative-unit the result has multiplicative-unit. + - If one operand has multiplicative-unit and the other not, the multiplicative-unit decays to unit `"1"`. + - There's a future refinement for the literal 0 (both real and integer) and `zeros()` in arrays see [advanced arrays](#advanced-array-handling) for details. +- If a constant is declared without unit, and with a binding equation that lacks units (even after inference) the constant is treated as empty unit. (This is primarily for package constants, where we don't want to infer a unit for `pi` and use it at unrelated places; but that also applies in models.) +- An expression having empty unit will match any constraint, and inference will not give it a unit. +- The rules for operands are fairly logical, but see appendix A in https://doi.org/10.3384/ecp21817 for the details. + +## Arrays + +Arrays with heterogenous units are somewhat rare but needed for state-space forms etc, and for some connectors in the electrical library. + +Without scalarizing arrays there are a number of options: +- Ignore units for arrays. +- Only infer units for elements, and arrays that are inferred/declared to be homogenous. (Proposed here.) +- Something more advanced. (Not proposed as it hasn't been tested.) + +This simple array handling implies that unit-handling is consistent between MultiBody mechanics and Rotational/Translational mechanics. + +### Advanced array handling + +The advanced array handling is not included yet as part of the proposal as it is not fully clear and not test-implemented, even if models ideally should fulfill something along these lines. + +It is included in this document, because there are some non-trivial issues if tools were to support this. +Based on the experience with existing models it will likely infer units for a number of variables, but find few, if any, new errors. + +One considerations is whether the arrays are just arrays or have more structure. +Many (likely most) arrays are used as vectors, matrices, etc in the linear algebra sense, but not all. +E.g., Modelica.Blocks.Tables.CombiTable2Ds has a table where the first row and column effectively has different units from the rest of the table. +It seems that tools could identify whether a 2d-array is used as a matrix (or even bilinear form) based on the equations, i.e. `A*x` imply that `A` is a matrix, and `x*A*x` that it is a bilinear form. + +The changes needed to support a more advanced array handling would be something like: +- Arrays built using `cat`, `[,;]` should (at least in some cases) support heterogenous inputs giving heterogenous unit-results, replacing parts of appendix A of https://doi.org/10.3384/ecp21817 +- Literal 0 (both real and integer) and `zeros()` inside arrays should be treated as the empty unit, and not decay to unit `"1"` as other literals without unit. +- If something is used as a matrix its units are restricted (it must be representable as an outer product); as noted above. +- Potentially more. + +The reason for the second rule can be seen from considering structured matrices in equations. +E.g., a simple state-space system without direct terms: +`[der(x);y]=[A,B;C,zeros(...)]*[x;u]` +(and correspondingly with a literal 0 if it has a single input and a single output). +In text books those zeros are often omitted, but that is not allowed in Modelica. + +Basically the zeros are seen as structural zeros and one would expand it as `y=C*x` (not `y=C*x+zeros(...)*u`), imposing no unit-constraint between `u` and `y`. +In contrast for `f1+f2=0*f1` it seems natural to have unit `"1"` for the literal, even if it is unlikely that someone makes a mistake for that equation. +And if one writes `[der(x);y]=[A,B;C,1]*[x;u]` then `u` and `y` should be scalars (or vectors of length 1) that both have the same unit. + +This also apply to the literal zeros in `diagonal()` and `skew()`, they are seen as having empty unit - not impacting the result. + +## Evaluable parameter + +The reason it applies for each value of the evaluable parameters is to make it sufficiently general to handle even models where evaluable parameters switch between different unit-configurations. +In practice the handling will depend on whether the parameter has been evaluated or not. + +### Evaluated evaluable parameter + +This is the simpler case, but still requires care, since expanding and evaluating expressions is normally mixed in tools. +For practical reasons one wants to perform the unit check on the original non-expanded expressions, where the evaluable parameters were not yet evaluated. + +This can be handled in various ways: +- Simplifying the original expressions based on the values of evaluable parameters. (In some sense this will be a form of double-work.) +- Having conditional constraints as in https://github.com/modelica/ModelicaSpecification/pull/3491. + +### Non-evaluated evaluable parameter + +This is a particular concern. +Tools should avoid having the unit-handling (except for the unit-attribute) cause evaluation of evaluable parameters. +Note that it doesn't suffice to separate parameters in evaluated and non-evaluated as different tools may evaluate different evaluable parameters with different result for unit consistency. + +However, in many cases it does not matter, e.g., an evaluable mass-parameter will have unit `"kg"` regardless of its value, and many boolean evaluable conditions don't influence the units. + +When it does matter (in particular for boolean conditions) it's a quality-of-implementation issue for tools to handle it in a good way, and possibilities include: +- Ignore the unit-constraints in such expressions. (Not good.) +- Temporarily evaluate the evaluable parameters, without impacting the translation. (This will only check the model for one set of values.) +- Treat them as conditional constraint in some advanced way. (This is more advanced that the conditional constraints in https://github.com/modelica/ModelicaSpecification/pull/3491 ) + +## Varying quality-of-implementation + +These are just rules for models, and doesn't require tools to diagnose all issues in models. + +The rules are compatible with: +- The traditional unit inference in Dymola (Mattsson&Elmqvist https://modelica.org/events/conference2008/sessions/session1a2.pdf ) +- Hindley-Milner for scalars (https://github.com/modelica/ModelicaSpecification/pull/3491) +- The advanced combination(s) thereof in https://doi.org/10.3384/ecp21817 + +They are seen as different quality-of-implementations, but we could recommend a minimum for tools. + +The unit-handling doesn't prioritize different operands (in contrast to selecting initial conditions and states), since that might give different results for the different levels for unit-consistent models. +For unit-inconsistent models it *possible* that using different set of rules will infer different units without detecting errors; the solution is to improve the quality-of-implementation to detect the underlying error in that case. +Additionally, this only occurs the different rules are not sub-sets of each other, so having a common understanding of hieararchy of the rules may reduce this risk. + +## Notes + +The rules for literals break the substitution principles for equality, so even if `A=[1,2;3,4]` it doesn't follow that `A` and `[1,2;3,4]` behave the same in terms of units. + +It says: +- "default rules" to allow allow stricter or less strict variants, e.g., as described in https://github.com/modelica/ModelicaSpecification/issues/3690 +- "except for the listed exceptions" to allow exceptions for specific equations etc https://github.com/modelica/ModelicaSpecification/issues/3690#issuecomment-2866443687 +- "multiplicative context", but it is for both multiplication and division (the division explains why the multiplicative-unit decays to `"1"` instead of just using the other one). + +Treating the empty unit-attribute as unspecified is needed, since it is the default - but it normally doesn't make sense to explicitly give `unit=""` for a variable declaration. +If the goal is just to remove an existing unit an alternative is `unit=break`. + +Specific exceptions for equations, and libraries, should preferably be added to the proposal. + +The restriction that variables only having one unit could be violated in different ways in models: +- Temporaries in algorithms in functions (and even models) may be re-used to store expressions with different units. +- The s-parametrization is an example of a variable that switches e.g., from voltage to current. + +For s-parametrization one solution is to divide out the unit and generate an expression with unit `"1"` and store that. \ No newline at end of file