Skip to content

Instantly share code, notes, and snippets.

@bilderbuchi
Last active December 15, 2022 10:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bilderbuchi/317bc1a2e039f477462a61f5826260ea to your computer and use it in GitHub Desktop.
Save bilderbuchi/317bc1a2e039f477462a61f5826260ea to your computer and use it in GitHub Desktop.
Proposed fix for dealing with literal constants in Modelica unit checking

Update proposal BB2a

This builds on my previous proposal, which was too wide in scope, and drops "empty units" from its scope, to retain the wildcard effect of variables with empty unit string.

In effect, we have to replace "expression containing only literal constants" with "expression containing only literal constants and mathematical operations". This deals with distinguishing between exp(...) and specificEnthalpy(...,...) situations.

This is a minimal-diff change. If agreeable, I propose a behaviour-neutral refactoring below (BB2b) that is a bit larger but makes the structure clearer.

Updated rule 1:

\item The result of \lstinline!x + L!, \lstinline!L + x\lstinline!, \lstinline!x - L!, \lstinline!L - x!, \lstinline!x*L!, \lstinline!L*x!, \lstinline!x/L!, where \lstinline!x! is an expression with non-empty \lstinline!unit! attribute string `<s>` and \lstinline!L! is an expression containing only literal constants and mathematical operations, shall have the \lstinline!unit! attribute string of \lstinline!x!.

See the next section for the definition of "mathematical operations".

Updated rule 4:

\item If either side of a relational operator is an expression containing only literal constants and mathematical operations, then it is assumed to have the \lstinline!unit! attribute string of the other side, if that is well-defined.

"Mathematical operations"

We need to add a definition for "mathematical operations". Please help refine this first definition:

"Mathematical operations" in the context of unit checking are

  • Arithmetic operators (3.4)
  • abs, sign, sqrt, Integer (i.e, 3.7.1 without String() and EnumTypeName())
  • Event Triggering Mathematical Functions (3.7.2)
  • Elementary mathematical functions (3.7.3)

I'm unsure about the role of 3.7.4 and 3.7.5.

Alternatively, maybe it's 3.4 + all of 3.7 except operations returning Strings or Enumerations?

Cf. the equivalent section "expressions with empty unit" of #3257, where we find

The following compound expressions result in the empty unit if and only if all operands have empty unit:
  \begin{itemize}
    \item \lstinline!Real!-valued numeric operator expressions.
    \item Function calls to \lstinline!Real!-valued built-in functions (see \cref{built-in-mathematical-functions-and-external-built-in-functions}, \cref{modelica:der}, \cref{modelica:pre}, \cref{modelica:abs}, \cref{modelica:sqrt}, \cref{modelica:floor}, etc.).
  \end{itemize}

Equality/assignment

I suspect (but am not sure) that we forgot to define the role of assignment/equality in unit checking (probably "correct" behaviour was assumed). If that's correct, we need to add a rule for that, see the relevant section in BB2b below. It was too tedious to formulate in the minimal-change context, but the intent is clear I think.

Proposal BB2b

To make the structure clearer and avoid repetition, some rearrangement was also included, and a rule concerning equality.

Add additional rules at the head of the list; avoid repeating what L and x are:

\item The result of mathematical operations *(precise scope TBD)* where all operands have undefined \lstinline!unit! string attribute has an undefined \lstinline!unit! string attribute.
(Non-normative addition: they behave just like literal constants themselves with respect to unit handling)
\item \lstinline!x! is an expression with non-empty \lstinline!unit! attribute string `<s>` and \lstinline!L! is an expression with undefined \lstinline!unit! attribute string.

See above for the definition of "mathematical operations".

Current rule 1 is now slimmer:

\item The result of \lstinline!x + L!, \lstinline!L + x\lstinline!, \lstinline!x - L!, \lstinline!L - x!, \lstinline!x*L!, \lstinline!L*x!, \lstinline!x/L! shall have the \lstinline!unit! attribute string of \lstinline!x!.

Current rule 4 can be cleaned up, too, not needing the "well defined" any more:

\item If a relational operator has \lstinline!x! and \lstinline!L! on opposite sides, \lstinline!L! has the same \lstinline!unit! attribute string as \lstinline!x!.

Equality/assignment

I suspect (but am not sure, see modelica/ModelicaSpecification#3266 (comment)) that we forgot to define the role of assignment/equality in unit checking (probably "correct" behaviour was assumed). If that's correct, we need to add a rule for that, too:

Concerning unit compatibility of equality \lstinline!a = b! and assignment \lstinline!a := b!:
\item If \lstinline!a!, \lstinline!b!, or both are of type \lstinline!L!, unit compatibility is given.

Additionally, we may want to also specify this (but maybe not, to limit PR scope)

\item If expressions \lstinline!a! and \lstinline!b! both are of type \lstinline!x!, their
\lstinline!unit! attribute strings need to be identical (TBC or just "compatible"?)

Note that we don't say anything about equality with "empty-unit-string" operands.

MCP-0027 Test examples

This is a collection of tests (contributed by @casella, @henrikt-ma, @bilderbuchi) that codify the desired behaviour of unit testing w.r.t. eliminating the so-called "wildcard effect" of literal constants.

The wildcard effect is preserved for variables defined with an empty unit string, for backwards compatibility and to avoid necessitating undesirably strict rigour in modelling.

The outcome of unit checking is either "accepted", "rejected" or "undecided" (when a wildcard effect remains).

With this proposal, we have the following outcome of dimensional analysis in these interesting cases (the units of variables are obvious from the context):

Dimensional consistency with simple quantities

With SI.Length L, SI.Length L2 and SI.Area A:

  • A = pi*d^2/4 is accepted
  • L = pi*d^2/4 is rejected
  • L2 = 3*L1 is accepted
  • L = 10.0 is accepted
  • L = 10.0*2 is accepted
  • L = 0.314 * 10^1 is accepted
  • L = 0.314e1 is accepted
  • L2 = L1/3 is accepted
  • L2 = 2*A/L1 is accepted
  • L2 = 2*A/L1^2 is rejected
  • L2 = 2*A/1.5 is rejected
  • L1 = L2 + 3 is accepted (Literal + x has the same unit of x)

Built-in mathematical operations

  • 2*w + der(w) is rejected
  • v = sqrt(2*g*h) is accepted (unit match between the output of sqrt and v)
  • v = sqrt(2*g) is rejected (unit mismatch between the output of sqrt and v)
  • i = i_0*exp(v/v0) is accepted
  • i = i_0/R*exp(v/v0) is rejected (result of exp() is dimensionless, but not empty - no wildcard)
  • i = i_0*exp(v/i0) is rejected
  • i = i_0*exp(v/3) is rejected
  • i = i_0*exp(10/3) is accepted
  • v_pp = sqrt(3)*v_pg is accepted
  • v_pp = sqrt(3)*i_pg is rejected

Proper functions

For proper functions, the rules that we have specified for handling the units of expressions containing literals should apply. Say that specificEnthalpy_pT(p,T) wants an input p with unit "Pa" and an input T with unit "K", and that we have defined variables p and T with such units, and h with unit "J/kg".

  • specificEnthalpy_pT(p, T) is accepted, because the input expressions have a well-defined unit, which is the right one
  • specificEnthalpy_pT(p, h) is rejected, because the second input has a well-defined unit, which is different from what is expected
  • specificEnthalpy_pT(2*p, T) is accepted, because 2*p has the same unit of p, i.e. "Pa"
  • specificEnthalpy_pT(p, T+2) is accepted, because T+2 has the same unit of T, i.e. "K"
  • specificEnthalpy_pT(p, h/1000) is rejected, because h/1000 keeps the unit of h, i.e. "J/kg"
  • specificEnthalpy_pT(1e5, 293.15) is accepted, because the inputs have no units information, so you can't say if they are dimensionally inconsistent
  • specificEnthalpy_pT(1e5*2, 293.15) is accepted, because the first argument still has no units information
  • specificEnthalpy_pT(1e5, 293.15 + time*20) is rejected, because the second input has unit "s" instead of "K"
  • 1e3 + specificEnthalpy_pT(p,T) is accepted because Literal + x has the same unit of x, i.e., "J/kg"
  • h + specificEnthalpy_pT(p,T) is accepted because it sums two quantities with unit "J/kg"
  • h + specificEnthalpy_pT(1e5, 293.15) is accepted because it sums two quantities with unit "J/kg"
  • T + specificEnthalpy_pT(p,T) is rejected because is sums a quantity with unit "K" and a quantity with unit "J/kg".
  • T + specificEnthalpy_pT(1e5, 293.15) is rejected as above. The fact that the inputs are solely literals does not have an influence.

Importantly, this last example is accepted by the current proposal e325f6f8b111afa3a1f87126a18d087ce6cd6d0b because of deficiencies of the formulation "expression containing only literal constants".

Undecidable cases

Real variables with empty unit attribute retain their wildcard behaviour. Unit checks where they are involved yield an "undecided" result (i.e., the checker gives up).

With Real E = 1 (empty unit string), SI.Volume V, SI.Volume V2 and SI.Length R:

  • V = A*E is undecided (E is a wildcard)
  • V = E is undecided (E is a wildcard)
  • V = 2*E is undecided (E is a wildcard)
  • V = A*2 is rejected (m3 != m2)
  • V = Modelica.Constants.gamma is undecided (constants like pi, etc have empty unit attributes)
  • V = 4*R^2*pi/3 is undecided (pi is a wildcard, error in sphere volume formula is not picked up)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment