ModelicaSpecification icon indicating copy to clipboard operation
ModelicaSpecification copied to clipboard

Configurable unit-handling

Open HansOlsson opened this issue 8 months ago • 8 comments

With more focus on good unit-checking we also need the possibility configure it, for the following reasons:

  • Support new base-units, such as currency https://github.com/modelica/ModelicaSpecification/issues/3425
  • Support enabling/disabling part of the unit-checking: concretely see particular https://github.com/HansOlsson/Modelica/tree/UnitTest

For enabling one can have a new annotation UnitChecking that can be placed on equations and possibly in classes

record UnitChecking
  /*literal*/ constant enable=true;
  /*literal*/ constant strictLiterals=true;
end UnitChecking;

Setting enable=false completely disable unit-checking, whereas strictLiterals=false disables the proposed 4-line idea.

Design issues:

  • An alternative is to have slightly different names for the equation and class ones, so that single a misplaced semi-colon can be detected.
  • An alternative is to have an enumeration with different options (Disable "completely disabled", IgnoreLiterals "Literals can have any unit", StrictMultiplication "The 4-line proposal"). That would work especially well if we can see them as ordered, but not if there are orthogonal concepts.

Tools should use the settings to limit the checking, and may also indicate that the checking was limited due to these settings. The main reason to limit the check is to support legacy code (without having to make too many changes - the goal is to have long-term support for Modelica models is that they should continue to work) and for correlations in a few equations. This is needed:

  • For specific equations as in https://github.com/HansOlsson/Modelica/tree/UnitTest
  • For entire packages

And new units and unit-conversions in (top-level) packages. The first can use:

record BaseUnit
  /*literal*/ String symbol;
  /*literal*/ String quantity;
end BaseUnit;

The latter could use the same logic as for version conversions but with a different command.

So:

package P
   annotation(BaseUnit(symbol="XXX", quantity="currency"), UnitConversion(script="test.mos"));
end P;

The goal would be to have some mild form of standardization of non-SI units; so that the same symbol and quantity are preferably used for the same things. (As in https://github.com/modelica/ModelicaSpecification/issues/3425 the "XXX" is used for currency, similarly as "USD" and "EUR").

Each BaseUnit should be orthogonal to ones that don't have the same symbol and quantity, and each symbol should only be matched with the same non-empty quantity. It might also be necessary to coordinate with the FMI-standard.

With conversion commands (no need for these specific ones, but to illustrate the concept):

// defineUnitConversion(<unit>, <derived unit>, <scale>, <opt. offset>); 
// if scale is zero the offset gives the inverted scale.
defineUnitConversion("W", "mW", 1000);
defineUnitConversion("K", "degC", 1, -273.15);
defineUnitConversion("cl/km", "mpgUS", 0, 378.5411784/1.609344)

The exact syntax can be discussed, and obviously tools may have global settings for units in other ways.

If the derived unit looks like a valid unit the specified conversion should be the correct one (so you cannot introduce "mW" to mean "10 A", but "mpg" is fine as you may not stack prefixes like "m" and "p" on top of the unit "g"). In particular duplicate identical unit conversions should just work.

The existing rules and guidelines for units still apply - including base units, and would become even more important.

HansOlsson avatar May 07 '25 15:05 HansOlsson

We have a complete annotation-only mechanism for per-library unit configuration. It would be interesting to discuss standardization of it, but this topic is way too big to be discussed within the same issue as a UnitChecking-annotation for restricting unit checking.

henrikt-ma avatar May 09 '25 10:05 henrikt-ma

We have a complete annotation-only mechanism for per-library unit configuration. It would be interesting to discuss standardization of it, but this topic is way too big to be discussed within the same issue as a UnitChecking-annotation for restricting unit checking.

I assume 'we' means 'Wolfram System Modeler'

Splitting this into two mechanisms is also possible and they could be discussed and added separately:

  • One or more annotation for libraries (potentially also for sub-libraries).
  • One annotation for equations to just disable the check (no need for anything else).

As indicated above having different names for these annotations actually avoids some stupid mistakes so it would be a good thing.

With a different name and the intent to just disable unit-checking the latter could replace

equation 
   x=y
  annotation(UnitChecking(enable=false));

by

equation 
   x=y 
   annotation(unitCheck=false);

(well, normally with a messier equation); see https://github.com/HansOlsson/Modelica/tree/UnitTest for actual uses.

HansOlsson avatar May 09 '25 12:05 HansOlsson

I am partial to annotation(unsafeUnit = true), or a noEvent style syntax even.

qlambert-pro avatar May 09 '25 12:05 qlambert-pro

We have a complete annotation-only mechanism for per-library unit configuration. It would be interesting to discuss standardization of it, but this topic is way too big to be discussed within the same issue as a UnitChecking-annotation for restricting unit checking.

I assume 'we' means 'Wolfram System Modeler'

Yes, but I should have made it more clear that I was speaking of the first item in the opening comment:

  • Support new base-units, such as currency

If the language group is interested in working on this feature, I propose that we have a kick-off meeting where I can present how the mechanism works in System Modeler, and representatives of other tools can also show similar mechanisms that they have.

We don't have any mechanism for opting out of unit checking except changing global tool settings.

henrikt-ma avatar May 09 '25 13:05 henrikt-ma

If the handling of literals should be configurable, I think an enumeration is needed to have an expandable set of options for describing a gradual progression towards a handling of literals more similar to the way literal works in all other languages/packages with support for units.

henrikt-ma avatar May 09 '25 13:05 henrikt-ma

I am partial to annotation(unsafeUnit = true), or a noEvent style syntax even.

Something like the first is fine by me.

I can understand the noEvent style syntax, but:

  • It would require some (minimal) changes of the actual equations, whereas I want to keep the equations as unchanged as possible (moving the semi-colon to the next line and adding an annotation seems like the minimum).
  • It would make sense if the unit-unsafe part was commonly deeply nested in an expression, but I didn't see such cases in MSL. (*)
  • The boundary would require some care when explaining. Consider e.g., x=y*unsafeUnit(b*12+c*13);, I would assume that unit-checking is disabled both inside unsafeUnit and also implicitly when using it since we don't know the unit of unsafeUnit(...). We have a similar issue with noEvent, with the choice between noEvent(if x>0 then x else 0) and if noEvent(x>0) then x else 0. In retrospect I would have preferred if only noEvent(if x>0 then x else 0) was legal.

*: Or more correctly, I didn't find them after close examination of MSL. As far I recall there's one case in https://github.com/HansOlsson/Modelica/tree/UnitTest where the modeller had assumed that the second term was unit-safe and the other terms unsafe - which would correspond to this even if not deeply nested, but as far as I can tell also the second term was unsafe as well. One can, of course, in most cases introduce a new equation for the problematic sub-expression. It is https://github.com/modelica/ModelicaStandardLibrary/commit/d2b31e05d68a54770cbd744ed6a4867f49fd3ce8

Updated: It was the second term that was assumed to be unit-safe (which is plain weird).

HansOlsson avatar May 09 '25 13:05 HansOlsson

Another alternative would be to have a new equation and a new statement block.

qlambert-pro avatar May 09 '25 13:05 qlambert-pro

If the handling of literals should be configurable, I think an enumeration is needed to have an expandable set of options for describing a gradual progression towards a handling of literals more similar to the way literal works in all other languages/packages with support for units.

Sure. To me the question of enumeration vs. flags depends on whether they can all naturally be ordered or not. One could have an ordering for strictness of literals like: 'literals have any unit' (MSL was checked using this previously), 'multiplicative literals have unit 1 others have any unit' (Casella proposal), 'multiplicative literals have unit 1, and other literals inside expressions except zero have unit 1' (special case for zero is common - or zero-case might be restricted to comparison), 'all literals inside expressions have unit 1', 'all literals have unit 1'. (Not sure if all of these are needed, or if I missed any relevant cases, the inside part is intended to still allow SI.Length len=10;.) But there might also be settings for checking functions, arrays, strictness of allowed units, etc that are separate from this.

And to me configuring this goes hand-in-hand with introducing the functionality with minimal disruption; as my experience is that otherwise non-strict libraries will try to disable the check in other ways that are more impactful; so that the non-strict library disables the check even for models not using the non-strict library.

HansOlsson avatar May 09 '25 14:05 HansOlsson