Skip to content

Avoid that Constants.pi gets the unit "rad" - #4774

Open
henrikt-ma wants to merge 2 commits into
modelica:masterfrom
henrikt-ma:pi-unit
Open

henrikt-ma wants to merge 2 commits into
modelica:masterfrom
henrikt-ma:pi-unit

Conversation

@henrikt-ma

@henrikt-ma henrikt-ma commented Apr 21, 2026 •

Copy link
Copy Markdown
Contributor

The current definition of Modelica.Constants.pi gives it the unit "rad", but based on the ongoing discussions about standardizing a definition of unit consistency in the Modelica specification, I find it clear that pi should have "empty" unit, making the constant behave just like a numeric literal.

For example, the following is expected to be allowed if pi has empty unit, but not if it has unit "rad":

Modelica.Units.SI.Time piTime = Modelica.Constants.pi "π seconds";

With the unit "rad", the workaround would be to insert a conversion factor,

Real conversionFactor(unit = "s/rad") = 1;
Modelica.Units.SI.Time piTime = conversionFactor * Modelica.Constants.pi "π seconds";

but I have a clear impression that our community wouldn't like to have to do it this way (even though it would be much more convenient once the factor can be given in-line as 1's/rad').

In this PR, the change for pi avoids that it gets the unit "rad", and e is updated to keep the two definitions consistent.

The change for 'pi' avoids that it gets the unit "rad", and 'e' is updated to keep the two definitions consistent.
@henrikt-ma

Copy link
Copy Markdown
Contributor Author

By the way, I am intentionally avoiding the question of what units to have on the interface of Modelica.Math.asin. In SystemModeler, for comparison, we are providing the experimental function SystemModelerExtras.Experimental.Units.Radian.asin with unit "1" on the input and unit "rad" on the output, for those who prefer to do their trigonometry with units.

@AHaumer

AHaumer commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

In my understand, "rad" is a pseudo unit as good as "1", just declaring "This is an angle, not a dimensionless ratio.".
With that point of view, "rad" is a helpful reminder.

@HansOlsson HansOlsson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue that pi logically should have unit "rad", e.g., when you call sin(2*pi*f*time) it makes sense that f is in Hz and time in seconds, so that pi ensures that the argument has unit "rad".

Obviously there are cases where radians for pi are a bit confusing.

That's why it is important BIPM states that radians exist (for clarity), but it is expressed as having unit "1" in base units.

Additionally, for a user seeing these lines it doesn't seem clear that asin instead of Modelica.Math.asin has such an interpretation. I would even say that the built-in asin should behave the same as Modelica.Math.asin in this respect, so in that sense it seems like a non-change.

@henrikt-ma

henrikt-ma commented Apr 22, 2026 •

Copy link
Copy Markdown
Contributor Author

In my understand, "rad" is a pseudo unit as good as "1", just declaring "This is an angle, not a dimensionless ratio.". With that point of view, "rad" is a helpful reminder.

Yes, I'd phrase it as "rad" being an alias for "1", which sounds more welcoming than calling it a "pseudo unit". However, this misses the point of this PR.

The point of this PR has to do with the sort of unit inconsistency you see here:

  Modelica.Units.SI.DimensionlessRatio x = 5; // x has unit "1"
  Modelica.Units.SI.Time t = x; // Inconsistent units!
  Modelica.Units.SI.Diameter d = x; // Inconsistent units!

If we want to use x in the declaration equations like that, it needs to come with the empty unit (since it can't be consistent with both "s" and "m"). The current design idea for unit checking in Modelica is that we can make x a constant with empty unit:

  constant Real x = 5; // x has empty unit

Applied to pi, there are many places where π is just a number. I got the following examples from Claude:

  • The Basel problem (∑1/n² = π²/6)
  • The Gaussian integral
  • Stirling's approximation
  • The volume of n-balls

Add to that the desire to easily use pi in Modelica empty unit expressions like in the example with x above.

To make all this work nicely, we need pi to have empty unit and just let it decay to "1" in the places where one wants to think of it as having "rad", thanks to "1" and "rad" being aliases in SI.

When speaking about angles, I personally prefer saying:

π radians is the same as 180°

not this:

π is the same as 180°

Then over to @HansOlsson's argument:

I would argue that pi logically should have unit "rad", e.g., when you call sin(2pif*time) it makes sense that f is in Hz and time in seconds, so that pi ensures that the argument has unit "rad".

Well, as long as "rad" is an alias for "1" there is no difference to ensuring the argument has unit "1". Thus, with pi having empty unit you would get the same amount of unit safety in this expression as when it would have unit "rad" (or "1" for that matter); in both cases it will be detected when the unit if f is not compatible with "1/s". Anyway, the ambiguity of how to interpret Hz is a much too big question to be solved here. For now I think we should just be happy that SI avoids most of these tricky issues by making "rad" an alias for "1".

@HansOlsson

Copy link
Copy Markdown
Contributor

Then over to @HansOlsson's argument:

I would argue that pi logically should have unit "rad", e.g., when you call sin(2_pi_f*time) it makes sense that f is in Hz and time in seconds, so that pi ensures that the argument has unit "rad".

Well, as long as "rad" is an alias for "1" there is no difference to ensuring the argument has unit "1".

Exactly. However, the exact handling of "rad" is a bit complicated, and it seems more discussion is needed.

I see two ways of handling "rad" in unit-checks:

  • Just use "rad" as an alias for "1".
  • Have "rad" work slightly different than "1", but decaying to it in some cases. (Dymola has implemented this since a long time - it kind of works, and is one of the reasons the FMI-standard has "rad" as a base-unit.)

I don't see that this PR gives any benefit with either alternative. Additionally, it relies on the yet-to-be-specified semantics of the built-in function asin instead of the MSL function with the same name. In my opinion they should work the same.

The problem I see with having pi treated similarly as a numeric literal is that it would seem to suggest that similarly as SI.Length len1=12; one could use SI.Length len2=pi;, and I don't see that the latter is desirable.

Basically I see three possible units for pi:

  • "rad"
  • "1"
  • empty unit similarly as numeric literal.

and my preference is clearly for the first one, with the second one as the second best.

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

I don't see that this PR gives any benefit with either alternative. Additionally, it relies on the yet-to-be-specified semantics of the built-in function asin instead of the MSL function with the same name. In my opinion they should work the same.

In the future, they might be made the same simply by removing all units from the interface of Math.asin, but as we all know we have some serious work to do for unit propagation through function calls before this can become reality. Until then, we need to make sure that at least the language function's unit design is clever enough.

One thing I find necessary that the language function supports is to take part in empty unit expressions, meaning that asin(1.0) should be usable anywhere an empty unit expression is allowed, for example when defining a constant that shall behave as an empty unit expression where referenced.

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

The problem I see with having pi treated similarly as a numeric literal is that it would seem to suggest that similarly as SI.Length len1=12; one could use SI.Length len2=pi;, and I don't see that the latter is desirable.

If we think about the other uses of pi that has nothing to do with angles, I don't find it strange that it can also be used instead of 3.14 to initialize a peculiar length. Yes, it is peculiar, but not particularly problematic. You can also initialize the length to other peculiar values such as $e$ given by exp(1.0).

@henrikt-ma

henrikt-ma commented Apr 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Maybe some of the disagreement could be resolved by also having a constant for one revolution:

final constant SI.Angle rev = 2 * pi "Angle corresponding to 1 revolution";

Then,

Real angle = f * rev * time; // Unit is "rad"
Real area = r^2 * pi / 2; // Unit is "m2", not "m2.rad"

@mestinso

mestinso commented Apr 22, 2026 •

Copy link
Copy Markdown
Collaborator

I'm firmly in the camp that pi should have unit="1", which was the original change made. I had thought that the plan was to revert to that once the implied unit issues throughout the library were resolved.

@henrikt-ma I'm also aligned with your last comment or at least the intent of your comment regarding how we could connect pi and angles.

And regarding this PR specifically: I don't have a problem wth it and would gladly approve if others see value, but I see it as a half measure. Instead, I think the unit for pi should be set back to 1.

@HansOlsson

Copy link
Copy Markdown
Contributor

If we think about the other uses of pi that has nothing to do with angles, I don't find it strange that it can also be used instead of 3.14 to initialize a peculiar length. Yes, it is peculiar, but not particularly problematic. You can also initialize the length to other peculiar values such as e given by exp(1.0).

You can - but it could also indicate that something is missing, and to me unit checking is intended to catch such issues (you might still do it - but then with some extra work arounds to indicate that you know what you are doing).

Consider an actual use case , Modelica.Electrical.Analog.Examples.SeriesResonance which has:

  Basic.Inductor inductor2(i(fixed=true), L=0.1/(2*pi))
...
  Basic.Capacitor capacitor2(v(fixed=true), C=0.001/(2*pi))

Using pi in that way can cause unit-issues (the goal is that SI.Frequency f0=1/(2*pi*sqrt(L*C)); corresponds to the 100Hz resonance frequency), but to me the solution is to update the calculations in some way instead of just viewing pi as having the empty unit in general.

Similarly for Modelica.Electrical.Analog.Examples.CauerLowPassAnalog that didn't/doesn't have pi, but had similar odd computations:

  parameter SI.Inductance l1=1.304 "Filter coefficient I1";
  ...
  parameter SI.Capacitance c2=1/(1.704992^2*l1)

@HansOlsson

HansOlsson commented Apr 22, 2026 •

Copy link
Copy Markdown
Contributor

I'm firmly in the camp that pi should have unit="1", which was the original change made. I had thought that the plan was to revert to that once the implied unit issues throughout the library were resolved.

Yes, and it is more than 2 years since #4191 was merged with that hope, and it's a pity that we haven't progressed further.

Updated: missed negation.

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

Basic.Inductor inductor2(i(fixed=true), L=0.1/(2pi))
...
Basic.Capacitor capacitor2(v(fixed=true), C=0.001/(2
pi))

Let's try the obvious:

  SI.Inductance L = 0.1'H' / (2 * pi);
  SI.Capacitance C = 0.001'F' / (2 * pi);
  SI.Frequency f0 = 1 / (2 * pi * sqrt(L * C));

You can have any power of "rad" in the unit of pi here; it will just cancel out in the unit of the f0 binding. That is, "1" is just as good as "rad" when it comes to these equations.

However, the effect of setting unit "1" for pi, as opposed to having empty unit, is primarily that any expression including a factor of pi can no longer be an empty unit expression. It will force the entire expression to suddenly have a consistent concrete unit. There can be many reasons for enforcing unit checking of an expression, but including a factor of π should not be one of them. For expressions where there already are units, the unit "1" or empty unit will make no difference.

@henrikt-ma

henrikt-ma commented Apr 22, 2026 •

Copy link
Copy Markdown
Contributor Author

I'm firmly in the camp that pi should have unit="1", which was the original change made. I had thought that the plan was to revert to that once the implied unit issues throughout the library were resolved.

Since then, I think we have developed a better idea for how units of constants should behave. The idea is that a constant with empty unit shall behave just like a numeric literal; there should be just as little "wild-card effect" when using the factor pi as when using the factor 3.14. An important part of the design is that the unit of a constant should only be determined as the unit of its binding expression (note that all constants must have a binding expression, so the constants can be sorted and unit-checked one after another); when the binding expression has empty unit, the constant gets the empty unit too, and will not get a unit inferred from how the constant is being used.

@AHaumer

AHaumer commented Apr 22, 2026 •

Copy link
Copy Markdown
Contributor

Just my 5 cent:

  SI.Inductance L = 0.1'H' / (2 * pi);
  SI.Capacitance C = 0.001'F' / (2 * pi);

This is a misleading formulation - I would avoid that.

  Basic.Inductor inductor2(i(fixed=true), L=0.1/(2*pi));
  Basic.Capacitor capacitor2(v(fixed=true), C=0.001/(2*pi));

This is really a bad example, frequency is missing. it should read as:

  parameter SI.Frequency f=1;
  Basic.Inductor inductor2(i(fixed=true), L=0.1/(2*pi*f));
  Basic.Capacitor capacitor2(v(fixed=true), C=0.001/(2*pi*f));

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

Just my 5 cent:

Your input is appreciated.

This is really a bad example, frequency is missing. it should read as:

  parameter SI.Frequency f=1;
  Basic.Inductor inductor2(i(fixed=true), L=0.1/(2*pi*f));
  Basic.Capacitor capacitor2(v(fixed=true), C=0.001/(2*pi*f));

That won't work out of the box since regardless of what of the unit alternatives we pick for pi, the f turns the expressions into unit business, and then the entire bindings need to have unit consistent with the modified components.

Anyway, I can see that 2 * pi * f looks like it is meant to be angular frequency, so it would be neat if it would have the unit "rad/s" and not just "1/s". However, in my mind the 2 * pi stands for "2π radians", and the introduction of the radian factor is not clearly seen in the expression. Since a syntax for attaching the unit "rad" to pi on the fly is not on the table at the moment (in System Modeler we would do it with the cumbersome operator form withUnit(pi, "rad")), this brings me back to using a more clear constant (trying a different name than rev this time):

final constant SI.Angle two_pi_rad = 2 * pi "Angle corresponding to 1 revolution";

So, two_pi_rad * f would clearly be an angular frequency, and unless we want to introduce more factors in the expressions, we end up with this to make units consistent:

L = 0.1'H.rad/s' / (two_pi_rad * f)

That looks strange to me, but maybe it somehow makes sense to you?

@mestinso

Copy link
Copy Markdown
Collaborator

ok, I'll bite.

So, looking at these recent examples, I want to rewind slightly to an upstream equation here that is connected to the equation that has been discussed.

So for inductors we have
$V = L \frac{dI}{dt}$
By all measures, this is dimensionally consistent, no explanation needed.

But now, for sinusoidal current, we can derive the relationship for inductive reactive:
$X_l = \omega L$
I won't show the derivation, but take note that in the process, we differentiated a sine function (we'll come back to this).

Looking at the units, and if we take units for angle seriously, we have
$[X_l] = \ohm$
$[L] = \ohm s$
$[\omega] = rad/s$

But wait, this apparently isn't dimensionally consistent anymore (Assuming we care about the angle units). What happened? Where did this $rad$ come from, and why isn't it cancelling out?

Following the logic in this article: https://iopscience.iop.org/article/10.1088/1681-7575/ac7bc2
... the answer is that the full equation for inductive reactance is actually
$X_l = C \omega L$
where $C$ has units and shows up when we differentiate a sine function. $C$ is unity but with units equal to $1/[rad]$. Additionally, this is actually fully generic for any angle measurements, and radians aren't special except for the fact that radians give a unity correction.
for frequency
$X_l = C f L$ and $C = 2 \pi / [Hz s]$
for degrees
$X_l = C \omega_{deg} L$ and $C = 2 \pi / 360 [deg]$

So we can see that we are dimensionally correct again (regardless of angle choice).

To conclude, you can see that it's not $\pi$ that we need to stick units on. Rather, it's that the equation itself has this $C$ term that shows up when we differentiate a sine or cosine function. This is not rigorously included 99.9% of the time. But if you follow the derivation and logic, you can see it resolves any angle unit inconsistencies.

Now, of course, every equation with angle units has a different story and way angle units are balanced out, but this is the story for this one.

@HansOlsson

Copy link
Copy Markdown
Contributor

This is really a bad example, frequency is missing. it should read as:

  parameter SI.Frequency f=1;
  Basic.Inductor inductor2(i(fixed=true), L=0.1/(2*pi*f));
  Basic.Capacitor capacitor2(v(fixed=true), C=0.001/(2*pi*f));

As noted above it is more that $f=1/(2\cdot\pi\cdot\sqrt{L \cdot C})$, so $(L 2 \pi)\cdot(C 2 \pi)=1/f^2$ or $C=1/f^2/(L 2 \pi)/(2 \pi)$.

There's clearly one degree of freedom here, i.e,, as long as the product of L and C stays the same it doesn't matter for the frequency. Whether that freedom is just used for setting L, C, or the ratio between them just depends.

If we use the square root of the ratio as the degree of freedom the formula is (unless I made some mistake):

  parameter SI.Frequency f=100 "As in problem description";
  parameter SI.Resistance R=10 "Not a real resistance or?";
  Basic.Inductor inductor2(i(fixed=true), L=R/(2*pi*f));
  Basic.Capacitor capacitor2(v(fixed=true), C=1/(R*2*pi*f));

Nice symmetry - I would say.
The problems are that it takes some effort to find it; and I'm not sure if it is better for users.

And as far as I understand we have understood that radians are useful in formulas (and pi sort of has that unit), but technically is the same as unit "1"; so we want to allow Area=pi*radius^2, but detect errors for Area=pi*radius and Area=pi .

@HansOlsson

Copy link
Copy Markdown
Contributor

For example, the following is expected to be allowed if pi has empty unit, but not if it has unit "rad":

Modelica.Units.SI.Time piTime = Modelica.Constants.pi "π seconds";

Thus, the relevant question is: is allowing that a good thing?

Consider the following model:

model Unnamed
  Real x(unit="1", start=1);
  Real v;
  parameter Modelica.Units.SI.Time timeScale=0.1;
  final parameter Modelica.Units.SI.Time onePeriod=(2*Modelica.Constants.pi*timeScale);
equation 
  der(x)=v/timeScale;
  der(v)=-x/timeScale;
  when time>=onePeriod then
    terminate("1 oscillation");
  end when;
end Unnamed;

(Should likely rewrite in some more advanced way, and have a better unit for x and v.)
And possibly change timeScale to 1.0 - you might have to change the stopTime.

After changing it to 1.0, one could skip the time-scale in both the equations and in the computation of onePeriod, making it look similar to piTime - but to me the unit handling is intended to find both that it was missing in the equations and in the computations of onePeriod. I'm fully aware that there are simplified models that fail that (cp. the original Modelica.Media.Examples.IdealGasH2O). That is not related to pi, but to people caring more about simplicity than about unit-consistency.

Note that I deliberately introduced it as timeScale - not TimeUnit; as it isn't intended to add a unit-factor "s" - but to correspond to an actual time-scale (or with a clearer name that captures the 2*pi part).

@casella

casella commented Apr 23, 2026 •

Copy link
Copy Markdown
Contributor

I'll repeat here what I already posted somewhere else in similar form, but can't find right now. Apologies for any duplicate content and for the long post.

1. Definition of pi

According to Wikipedia and to any mathematics textbook I know of, $\pi$ is defined as the ratio of a circle's circumference to its diameter. Both the circumference and its diameter are undoubtedly lengths. Whatever unit they are measured with, the ratio of two lengths is a nondimensional quantity in per unit. Hence, IMHO the unit of $\pi$ should be "1".

Unfortunately, this is far from settling the matter, see below.

2 The unit Entscheidungsproblem

I may be mistaken, but I am afraid that with units in Modelica 3.8 we are repeating David Hilbert's mistake. Hilbert (one of the greatest mathematicians of all time, mind you) had a grand plan to completely formalize mathematics. In particular, he argued that the Entscheidungsproblem could be solved mechanically, by an algorithm. Until Alonso Church and Alan Turing proved this was not possible. I'm of course not even remotely as smart as Church or Turing, but I'd like to try doing the same in this context 😅

My fundamental argument is that, when considering physical equations, context matters. Unfortunately, context is not completely formalized: a Modelica compiler does not understand what equations actually mean and where they come from. This is a big deal.

2.1 An example problem, which formalizes requirements on dimensional consistency checking

Consider the following Modelica code. It contains some basic equations from mechanics, which are correct and thus also dimensionally consistent, as well as some equations which are not dimensionally consistent, so they are for sure wrong and ought to be rejected by a unit-conscious compiler.

Some equations are redundant, but that's not an issue here, nor is the variability of the variables (constants vs. parameters vs. time-varying variables). The only thing we care about in this model is the dimensional consistency of the equations, not their solvability.

model Examples
  constant Real pi =  3.141592653589793;
  Real D(unit = "m") "Diameter of the wheel";
  Real p(unit = "m") "Perimeter of the wheel";
  Real r(unit = "m") "Radius of the wheel";
  Real phi(unit = "rad") "Rotation angle of the wheel";
  Real phi_t(unit = "rad") "Target rotation angle of the wheel";
  Real w(unit = "rad/s") "Angular velocity of the wheel";
  Real T(unit = "s") "Period of rotation of the wheel";
  Real f(unit = "Hz") "Frequency of rotation of the wheel";
  Real L(unit = "m") "Distance travelled by the wheel";
  Real v(unit = "m/s") "Translational velocity of the wheel";
  Real J(unit = "kg.m2") "Moment of inertia of the wheel";
  Real M(unit = "kg") "Mass of the wheel";
  Real tau(unit = "N.m") "Torque applied to the wheel";
  Real E(unit = "J") "Mechanical energy of the wheel";
  Real P(unit = "W") "Power applied to the wheel";
  Real pi;
equation
  // Dimensionally consistent equations, should be accepted
  der(phi) = omega;  // (1)
  der(phi) = 2*pi*f ;  // (2)
  der(L) = pi*D*f;  // (3)
  J*der(w) = tau; // (4)
  der(E) = P;  // (5)
  p = pi*D;  // (6)
  p = 2*pi*R;  // (7)
  D = 2*r;  // (8)
  f = 1/T;  // (9)
  L = phi*r;  // (10)
  E = J*omega^2/2 + M*v^2/2; // (11)
  P = omega*tau; // (12)
  phi_t = pi; // (13)

  // Dimensionally inconsistent equations, should be rejected
  der(phi) = 2*pi*f^2; // (14)
  p = pi*D^2;  // (15)
  p = 2*pi*R^2;  // (16)
  D = 2*r^2;  // (17)
  f = 1/T^2;  // (18)
  L = phi*r^2;  // (19)
  der(E) = omega;  // (20)
end Examples;

Whatever decision we take on the unit of $\pi$ and on how we handle units in Modelica in the future, my requirement as a modeller is that equations (1)-(13) should be accepted, since that is what any first-year engineering student would write, straight out of a physics textbook, whereas equations (14)-(20) should be rejected, because they are dimensionally inconsistent and thus they are for certain wrong.

I assume we can all agree about this requirement. If not, then I would suggest to discuss this matter separatly and settle it down first, because that's a pre-requisite for any further discussion.

2.2 What is the unit of $\pi$?

In this ticket, I read arguments in favour of:

  1. constant Real pi = 3.141592653589793 "unitless";
  2. constant Real pi(unit = "1") = 3.141592653589793 "dimensionless, per-unit";
  3. constant Real pi(unit = "rad") = 3.141592653589793 "pi is a flat angle, so radians";

As I understand, unfortunately none of these choices is acceptable, because each one leads to bad consequences.

  • If we choose 1., then pi becomes a wildcard, so (15) will not be rejected. As to (16), it will not be rejected as well, though this issue compounds with the status of literal constants MCP-0027 Units of Literal Constants ModelicaSpecification#2127, which is still not settled.
  • If we choose 2., (13) will be rejected, because the LHS has unit "rad" and the RHS has unit "1"
  • If we choose 3., (10) will be rejected, because the LHS has unit "m" and the RHS has unit "rad.m"

The crucial point here is that there is no single choice of units for $\pi$ that can make both (10) and (13) unit-consistent. Please let that sink in. There's nothing wrong with equations (10) and (13): one is basic kinematics and another one is just setting a threshold angle to $\pi$. Unfortunately, deciding about their correctness is only possible if you consider what these equations mean. That information is not included in the unit strings. Maybe it's a good job for AI, if you are willing to accept the occasional hallucination (I don't, for the record).

The only way out of this quagmire that I can see is to consider both "rad" and "1" as dimensionless units, so they are effectively equivalent when doing dimensional consistency checks, not unit checking. If we do so, then both 2. and 3. ar acceptable, though I'm slightly in favour of 2. because of how $\pi$ is defined. 1. remains unacceptable because it allows clearly invalid equations to be accepted.

2.3 Is it possible to automatically decide whether a Modelica equation is dimensionally consistent or not?

As far as I understand, by looking at my Examples above, this is indeed possible, but only if:

  • we consider "rad" and "1", as well as "Hz" and "s-1", as interchangeable
  • we define $\pi$ to either have unit "1" or "rad"
  • we find a means to make sure that the factor 2 is considered as a non-dimensional factor, not as a wildcard

2.4 Is it possible to automatically decide whether a Modelica equation is unit-consistent?

This task is trickier than just determining dimensional consistency. Consider this example

model Examples2
  Real Ltot(unit = "m") "Total length";
  Real L1(unit = "m") "Length of first part";
  Real alpha(unit ="1") "Fraction of part to total, in p.u.";
  Real beta(unit = "%") "Fraction of part to total in p.u.";
equation
  L = alpha*Ltot; // (1)
  L = beta*Ltot // (2)
end Examples2;

Equation (1) is correct, and in fact has units "m" and "m.1" = "m" on both sides, whereas equation (2) is wrong, and in fact it has unit "m" on the LHS and unit "m.%" on the RHS, so there is a factor 100 missing to make it unit consistent. Note, however, that also (2) is dimensionally consistent.

Now, what I determined before is that in order to fulfill my requirements on Example, you need to forgo the difference between "rad" and "1", which are both non-dimensional but differ by a factor $\pi$. So, in general, automatically finding if the proper factors are in place (or performing unit conversions automatically if they don't) doesn't seem to me something that can always be done. At least when quantities involving rotation and angles are involved.

As far as I understand, this fundamentally undermines the possibility of general, rigorous and automatic unit-checking and automatic unit conversions in Modelica.

3. Summary

I'm afraid I don't have the time and the compentences to get into the details of unit checking algorithms. My contribution to the whole unit discussion, as a modeller, is to provide the Examples model as a litmus test. My requirement is that the first batch of equation should be accepted, and the second rejected. I hope there's agreement on that, I will open a separate ticket to formalize this in the Modelica Specification issue tracker, where it belongs.

To my understanding, if that requirement is fulfilled:

  • it is only possible do perform dimensional consistency checking, not unit checking, in general.
  • whether the unit of Modelica.Constants.pi is "rad" or "1" doesn't really matter, as both are dimensionless units.

My 2 cts 😃

@HansOlsson

Copy link
Copy Markdown
Contributor

3. Summary

I'm afraid I don't have the time and the compentences to get into the details of unit checking algorithms. My contribution to the whole unit discussion, as a modeller, is to provide the Examples model as a litmus test. My requirement is that the first batch of equation should be accepted, and the second rejected. I hope there's agreement on that, I will open a separate ticket to formalize this in the Modelica Specification issue tracker, where it belongs.

Good. One could add p = 2*pi; // (16B)

To my understanding, if that requirement is fulfilled:

  • it is only possible do perform dimensional consistency checking, not unit checking, in general.
  • whether the unit of Modelica.Constants.pi is "rad" or "1" doesn't really matter, as both are dimensionless units.

My 2 cts 😃

Agreed, so the unit-checking has to sort of accept that "rad" and "1" can be mixed, but still try to differentiate between them when possible.

@AHaumer

AHaumer commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

I totally agree with @casella looking at his chapter 2.3, tht's what I meant.

@AHaumer

AHaumer commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

The problem of removing "rad" that I see for the usage in drives:
The rotational speed n is given in "1/s" (or even rev/s).
The angular velocity w is given in "rad/s".
w and n both stand for the same effect: rotation, but you have to know "which unit".
Easy to convert: w = 2pin; Therefore pi has the unit "rad" (and I keep in mind that "rad" is equivalent to "1").
I told my students: In Modelica internally we use w in rad/s, but for humans it's easier to deal with n in 1/s.
With w is easier to calculate speed of a rolling wheel: v = rw; (not using 2pi*n).

@henrikt-ma

henrikt-ma commented Apr 23, 2026 •

Copy link
Copy Markdown
Contributor Author

No! This is what I made very clear above: Our design work for unit consistency has converged to a good design long time ago:

  • Literals, having "empty unit", are not acting as multiplicative wildcards in equations; they "decay to unit 1".
  • Constants may also have "empty unit", enabling us to use a constant named pi instead of writing 3.1415926535897931 everywhere.
  • Do not mistake the "empty unit" for being the state of a unit which has yet to be inferred!

Yes, there are remaining topics in Modelica unit consistency which are still being debated, but I have a very clear picture of the handling of constants not being one of them.

Regarding

  phi_t = pi; // (13)

we have too much legacy to be able to reject

  phi_t = 3.14;

so therefore (13) will also be accepted when pi has empty unit.

I don't see what this has to do with the unit of pi:

  L = phi*r;  // (10)

However I just want to point out that, as @mestinso explained in #4774 (comment), this sort of relation may actually be missing a hidden factor with unit, and because of the widespread use of sloppy relations like this we end up being forced to make the radian an alias for the unit 1.

Also remember that when comparing to other literature on units, there may be no distinction between a numeric value and a "value of quantity" with unit 1. In Modelica this distinction is important, since we have a long tradition relying on the so-called empty unit expressions being accepted where the unit 1 would be wrong. For example,

Real p(unit = "m") = 6.28; // OK
Real p(unit = "m") = 6.28'1'; // Inconsistent

Hence, adequate support for also working without units is important in Modelica, and forcing the user to just throw units away is not adequate support. Failure to stay in the unitless world should be rejected in the same way as unit inconsistency is rejected. If the MSL provides a pi that cannot be used in empty unit expressions, then I am afraid that other libraries will define their own pi which they can use just like a numeric literal.

I just can't see the big point of rejecting

Real p(unit = "m") = 2 * pi;  // (16B)

and forcing the user to use a literal instead:

Real p(unit = "m") = 2 * 3.14;

@mestinso

Copy link
Copy Markdown
Collaborator

It seems there are two separate discussion points here:

  1. Units of pi --> still think "1" is the right choice..
  2. Assigning with literals. This issue seems to be beyond just pi. What if I want to define a length that has the numeric number equal to the speed of light? Isn't this the same situation?
    Let's take that case (to not conflate with the first issue):

In dymola i see the following:

Real p(unit = "m") = c; // option 1: this gives unit warnings (seems reasonable)
Real p(unit = "m") = 1*c; // option 2: this is ok (seems reasonable, 1 has an implied conversion)
Real p(unit = "m") = 1.0*c; // option 3: this is ok (seems reasonable, 1.0 has an implied conversion)

Why don't we have the same logic for pi? Give it unit="1" and the same way out if we want to set the length equal to pi?

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

In dymola i see the following:

Real p(unit = "m") = c; // option 1: this gives unit warnings (seems reasonable)

So far so good.

Real p(unit = "m") = 1*c; // option 2: this is ok (seems reasonable, 1 has an implied conversion)
Real p(unit = "m") = 1.0*c; // option 3: this is ok (seems reasonable, 1.0 has an implied conversion)

Here Dymola is not aligned with where the standardization is converging to; there must not be a "wild-card" effect when including a numeric literal as a factor. With its current behavior it I guess it is only rejecting (15), (18) and (19) on @casella's list. Maybe Dymola will be more aligned in its next version?

By the way, with the unitful literals from modelica/ModelicaSpecification#3688 it will be easy to resolve the inconsistencies like so:

Real p(unit = "m") = 1's' * c;

Once we have unitful literals, it could be argued that this is bad style and should be rejected:

Real p(unit = "m") = 4;

just because there is a cleaner way to do it:

Real p(unit = "m") = 4'm';

However, since the variant with an empty unit expression (often a literal) is still today the only way of doing it in Modelica, the legacy is enormous and it is too late to get it "right". However, as long as the unit is immediately available from context, it isn't really a problem to allow an empty unit expression to implicitly be associated with the unit from the context. (It is the use of empty unit expressions where the unit is not immediately available from context that is the real problem, and here I don't see an enormous legacy that would prevent us from gradually phasing it out from Modelica.)

This the reason why it is so important for the definition of unit consistency in Modelica that there are ways to work both in the physical domain of values of quantity (expressions with unit), and in the mathematical domain of real numbers (expressions with empty unit). Also, there must be a clear separation of the two so that there is no question whether an expression has empty unit allowing it to take on a unit from context or an expression with unit that must be used consistently. In Modelica, giving pi a unit is to not support working in the mathematical domain of real numbers.

Why don't we have the same logic for pi? Give it unit="1" and the same way out if we want to set the length equal to pi?

It would be thanks to actually having unit "1" for pi that Dymola would reject (15), but the point is that we don't need it to reject all of (14)–(20). All we need is that empty unit factors don't not turn into wildcards, and this mechanism is sufficient to also reject (15) also when pi has empty unit.

I don't see a strong analogy between π and c. While π appears in a variety of purely mathematical relations on real numbers, c is used in physical relations involving values of quantity. π is not only the ratio between the circumference of a circle of radius 4m and its diameter, it is also the ratio between the real unit circle's circumference and radius. To support both uses in Modelica, it should behave as a numeric literal.

@mestinso

Copy link
Copy Markdown
Collaborator

@henrikt-ma To clarify, I understand that today pi doesn't have units on it, so the behavior is not comparable to the speed of light behavior. What I mean to say or am trying to imply is that I'd like the behavior to be analogous.

So yes, I'd like to see this future state after we have the unitful literals features come online:
I'd like both of these to work:

Real p(unit = "m") = 1's' * c;
Real p(unit = "m") = 1'm' * pi;

And I'd like both of these to give a unit warning:

Real p(unit = "m") = c;
Real p(unit = "m") = pi;

...and until that new unitful literals feature comes online, I'm ok with this being a sort of workaround:

Real p(unit = "m") = 1 * c;
Real p(unit = "m") = 1 * pi;

@HansOlsson

Copy link
Copy Markdown
Contributor

It seems there are two separate discussion points here:

  1. Units of pi --> still think "1" is the right choice..
  2. Assigning with literals. This issue seems to be beyond just pi. What if I want to define a length that has the numeric number equal to the speed of light? Isn't this the same situation?
    Let's take that case (to not conflate with the first issue):

In dymola i see the following:

Real p(unit = "m") = c; // option 1: this gives unit warnings (seems reasonable)
Real p(unit = "m") = 1*c; // option 2: this is ok (seems reasonable, 1 has an implied conversion)
Real p(unit = "m") = 1.0*c; // option 3: this is ok (seems reasonable, 1.0 has an implied conversion)

Just to clarify - that is with default settings (and after import Modelica.Constants.c).

If you set Advanced.Modelica.CheckUnitsProposedSimple = true; they all give similar diagnostics, as a multiplicative literal (like 1 or 1.0) is then assumed to have unit "1".

Note that setting that flag will trigger lots of diagnostics from MSL, and I would have hoped we could focus on actually improving them to help users - and to find potential errors. (There are some PRs to improve the situation even for the 3.6 version of the Modelica Language; like #4591 #4398 #4051 there are some marked requires37 and then for additional proposals we have something like https://github.com/HansOlsson/Modelica/tree/UnitTest2 - their might be more needed for pi - I haven't investigated that in detail)

Obviously, there can be different opinions on whether Real p(unit = "m") = 1*c; should be legal or not. One of the items to work on for the specification to ensure that there are clear ways to configure that when needed (in ways yet to be specified).

The constant issue (in particular for package constants) is that they should only infer unit from their declaration not from their use, since something like "constant Real eps=2...e-16;" and then have assert(length>eps, "Length cannot be too small"); and `assert(voltage>eps,"Voltage cannot be too small"); seems to cause more problems than it solves.

Obviously some constants like c should have unit.

Why don't we have the same logic for pi? Give it unit="1" and the same way out if we want to set the length equal to pi?

That would be good (or using "rad" as indicated before), but it requires actually working on improving MSL to be better at handling units.

Simply put - pi should have unit "rad" or "1"; at least in the future. Now we need to work on improving MSL.

@HansOlsson

Copy link
Copy Markdown
Contributor

The problem of removing "rad" that I see for the usage in drives: The rotational speed n is given in "1/s" (or even rev/s).

(And in some cases in "rev/min".)

Note that the SI-standard has another solution for this: "Hz" instead of "1/s".
To quote the SI-brochure:

The hertz shall only be used for periodic phenomena and the becquerel shall only be used for stochastic processes in activity referred to a radionuclide.

So, if you have "rad/s" and "Hz" it is clear which is which, but when we infer units we often get "1/s" and don't know if it is one, the other, or something completely different (and don't use becquerel just because it is "1/s"). (We might have some over-use of "rad" in Dymola.)

That's why we have:

  type Frequency = Real (final quantity="Frequency", final unit="Hz");
  type AngularFrequency = Real (final quantity="AngularFrequency", final unit="rad/s");

@henrikt-ma

henrikt-ma commented Apr 27, 2026 •

Copy link
Copy Markdown
Contributor Author

And I'd like both of these to give a unit warning:

This is where we need the discussion to be focused, and we need two more variations on the theme:

Real p(unit = "m") = c; // c is speed of light
Real p(unit = "m") = 3.14;
Real p(unit = "m") = pi; // pi is π
Real p(unit = "m") = 5 * sin(pi / 3); // Intention: (5 * 0.866…) meters

Ultimately, I think we must accept that the unit of asin(1.0) (a language design question) might not have to be the same as that of Modelica.Constants.pi (a MAP-Lib design question). Right now, however, I think we are too few people involved in this discussion to be able to draw conclusions. I think it would be an excellent topic to bring up on the next MAP-Lib monthly meeting, don't you think, @casella?

Edit: I added one more variation where pi is used in a more complicated expression which needs to end up with either unit "m" or empty unit.

@mestinso

Copy link
Copy Markdown
Collaborator

Ultimately, I think we must accept that the unit of asin(1.0) (a language design question) might not have to be the same as that of Modelica.Constants.pi (a MAP-Lib design question).

Agreed. I think the following reference lays it out the best: https://iopscience.iop.org/article/10.1088/1681-7575/ac7bc2/pdf

The following screenshots demonstrate the basic idea for unit handling on trig functions.

image image

Unitwise, the trig functions in modelica are the standard "rad" variety. So sin, cos, tan should expect unit="rad" as input, and return unit="1" as the output. And the inverses asin, acos, atan, should expect unit="1" as the input and return unit="rad" as the output.

And pi itself is dimensionless/unit="1"

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

Agreed. I think the following reference lays it out the best: https://iopscience.iop.org/article/10.1088/1681-7575/ac7bc2/pdf

Yes, they make it very clear that π is not an angle by itself, and I love the way they stress the importance of "complete functions". However, they do not pay much attention to "analytical angle" (introduction to section 4) and dimensionless functions (section 4.3.4), while the Modelica ecosystem comes with a legacy that require a much stronger recognition of these concepts.

And pi itself is dimensionless/unit="1"

The point is that in Modelica, we need to make a real distinction between dimensionless (empty unit) and "1". Since it is a weirdness of Modelica, we can't expect the metrologists to solve this problem for us, and it is not surprising that the paper doesn't put emphasis on this distinction.

Besides the weirdness of allowing an expression with empty unit to take on a unit given by the context, computations with empty units also have an important role to play for empirical relations that relate values of quantities when expressed in some particular choice of units, and if we are serious about supporting cyber-physical systems (section 1.1 in the specification) I think it has a value in itself to properly support computations on real numbers without any presence of units.

It is in this context that I would like to have a constant for π which may be used in all those truly dimensionless computations where there should be no presence of units whatsoever.

By the way, it is also nice to see the very clear notation they use for the transition between quantities and "coefficients" in section 2, which we are also able to express using the experimental operators in System Modeler. Take the angle a and the unit "deg" for example, then regardless of the unit of a, it is the same quantity as withUnit(withoutUnit(a, "deg"), "deg"). These operators give a controlled way of transitioning between physical modeling and crunching of real numbers.

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

Regarding the built-in trigonometric functions in Modelica, one could actually gain some unit safety by only supporting "analytical angle", corresponding to empty unit. This would avoid the need for somewhat complicated overloaded behavior, while functions such as sin_rad can act as a geometric angle wrapper:

function sin_rad
  input SI.Angle u;
  output Real y(unit = "1") = withUnit(.sin(withoutUnit(u, "rad")), "1");
end sin_rad;

(The use of withUnit above only makes explicit what would otherwise depend on the Modelica "legacy" treatment of empty unit expressions.)

The degree variant of sin would look almost identical:

function sin_deg
  input NonSI.Angle_deg u;
  output Real y(unit = "1") = withUnit(.sin(withoutUnit(u, "rad")), "1");
end sin_deg;

With some intelligence in unit inference for function calls, one could even make a generic variant:

function sin
  input u(quantity = "Angle");
  output Real y(unit = "1") = withUnit(.sin(withoutUnit(u, "rad")), "1");
end sin;

This would actually be the safest variant of all, as withoutUnit(u, "rad") will not allow u to have empty unit. The variants sin_rad and sin_deg will silently accept empty unit arguments without knowing whether that number was meant to have the correct unit for the function at hand.

@HansOlsson

Copy link
Copy Markdown
Contributor

Unitwise, the trig functions in modelica are the standard "rad" variety. So sin, cos, tan should expect unit="rad" as input, and return unit="1" as the output. And the inverses asin, acos, atan, should expect unit="1" as the input and return unit="rad" as the output.

Agreed.

And pi itself is dimensionless/unit="1"

Dimensionless for sure, but I think we need to consider "1" or "rad" this a bit more, and to the most pragmatic solution (after fixing unit issues in MSL like merging #4780 - and more) is to make Modelica.Constants.pi correspond to $\pi \text{ rad}$ in that paper.

That is consistent with all of the uses of Modelica.Constants.pi/2 (etc) for angles, as people don't write $\pi/2 \text{ rad}$ in Modelica (and it is not entirely clear how to write it), and to me it seems that the majority of uses of pi are for angles. The downside is that circle areas and areas of circle segments either gets awkward units (that tools can handle) or one needs to convert the angle in "rad" to the same number with unit "1" (corresponding to the SI-definition that "radian" is just a dimensionless number).

But, that is not urgent as we first need to correct the units of MSL in general; and I still find it sad that we spend so much time on this - and so little time on actually fixing the real unit issus in MSL.

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

But, that is not urgent as we first need to correct the units of MSL in general; and I still find it sad that we spend so much time on this - and so little time on actually fixing the real unit issus in MSL.

I opened this because of a unit error in a library caused by pi being used where an empty unit expression was required.

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

Dimensionless for sure, but I think we need to consider "1" or "rad" this a bit more, and to the most pragmatic solution (after fixing unit issues in MSL like merging #4780 - and more) is to make Modelica.Constants.pi correspond to π  rad in that paper.

At least with this way of understanding π in the paper – as something to which a unit can be attached – we get that π in the paper would correspond to an empty unit 3.14… in Modelica. So far so good.

But to then say that the MSL should have a pi constant which represents π rad would mean to break alignment with the very nice analysis they do in the paper. Sounds like asking for trouble, and very confusing in my opinion.

If we would only get those unitful literals in the language, and if we additionally get implicit unit conversion in some places where it is safe, then there will be no more need to write Modelica.Constants.pi / 2 to express an angle when setting an SI.Angle parameter; simply saying 90'deg' will be so much better.

@HansOlsson

HansOlsson commented Apr 29, 2026 •

Copy link
Copy Markdown
Contributor

Added:

At least with this way of understanding π in the paper – as something to which a unit can be attached – we get that π in the paper would correspond to an empty unit 3.14… in Modelica. So far so good.

That is not correct. The paper has π as having unit "1" - so that giving an angle as π rad gives it the unit "1*rad".

No-one else is considering π as having unknown unit (except as a short-term solution), the interesting discussion is whether it is "1" or "rad" - and/or how to be even smarter.

But, that is not urgent as we first need to correct the units of MSL in general; and I still find it sad that we spend so much time on this - and so little time on actually fixing the real unit issus in MSL.

I opened this because of a unit error in a library caused by pi being used where an empty unit expression was required.

I'm aware that it may cause problem currently - even if I haven't seen the specific case causing the problems in MSL.

The long-term solution is to improve the formulas so that pi can have unit "1" or "rad", I don't see any reasons against that. So, if someone writes SI.Length perimeter=Modelica.Constants.pi;, I see that as a missing the radius (that could be 1 meter); not as a good model.

Thus, it's not just a matter of mechanically adding units - but to use unit-checking as a tool to find where to improve the models, and then check correctness.

That's why I proposed:

  parameter SI.Frequency f=100 "As in problem description";
  parameter SI.Resistance R=10;
  Basic.Inductor inductor2(i(fixed=true), L=R/(2*pi*f));
  Basic.Capacitor capacitor2(v(fixed=true), C=1/(R*2*pi*f));

(the resonance frequency is f=1/(2*pi*sqrt(L*C))) instead of the original:

  Basic.Inductor inductor2(i(fixed=true), L=0.1/(2*pi));
  Basic.Capacitor capacitor2(v(fixed=true), C=0.001/(2*pi));

To me that can make the examples easier to understand - instead of just pulling numbers out of thin air, and allows experimentation with the models (how far can we change f, how does R impact the resonance?), but it takes time and effort.

I could understand if we for the short-term set the unit to "" for pi, but then it shouldn't rely on asin being different from Modelica.Math.asin, and it should be clear that it is a short-term solution for compatibility reasons. Alternatives for that include: be more restrictive in how constants get units from their bindings (for the time being) to exclude this case, and/or use some unit-compatibility annotation on this declaration.

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

It is very frustrating that we are not making progress here. Today I had a bunch of unit inconsistencies which would have been perfectly solved by multiplication with Constants.D2R, but that idea turns out to not work since D2R itself has a unit error due to the use of pi in its definition:

  final constant Real pi = 2*Modelica.Math.asin(1.0); // 3.14159265358979;
  final constant Real D2R(final unit="rad/deg") = pi/180 "Degree to Radian";
  final constant Real R2D(final unit="deg/rad") = 180/pi "Radian to Degree";

The definitions of D2R and R2D would have worked exactly as intended if pi would have behaved as just a good name for 3.14159265358979 – precisely according to what the comment says. Having a constant for this purely numeric value is just too useful to not have a place in Modelica.Constants, and there is no better name for it than pi.

I obviously don't want to sign off on pi with empty unit being just a temporary solution, but a compromise would be better than nothing.

I also want to avoid this PR getting stuck because of the question whether the built-in language function asin could be used in empty unit expressions while Modelica.Math.asin cannot, so a pragmatic solution would be to avoid all kinds of asin in the definition and just put in more digits than anyone would need in Modelica for the foreseeable future:

  final constant Real e  = 2.71828182845904523536028747135266249;
  final constant Real pi = 3.14159265358979323846264338327950288;

@HansOlsson

Copy link
Copy Markdown
Contributor

It is very frustrating that we are not making progress here. Today I had a bunch of unit inconsistencies which would have been perfectly solved by multiplication with Constants.D2R, but that idea turns out to not work since D2R itself has a unit error due to the use of pi in its definition:

  final constant Real pi = 2*Modelica.Math.asin(1.0); // 3.14159265358979;
  final constant Real D2R(final unit="rad/deg") = pi/180 "Degree to Radian";
  final constant Real R2D(final unit="deg/rad") = 180/pi "Radian to Degree";

I thought the solution is to change 180 to 180'deg', or?

@henrikt-ma

henrikt-ma commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

I thought the solution is to change 180 to 180'deg', or?

Not as I see it, as that paints us even more into the corner of pi carrying unit.
Edit: I take that back, it would work with or without a unit on pi.

However, there is a similar but much nicer approach which is a general recipe for all sorts of unit conversion factors (and which I believe should be used extensively in the Modelica.Units.Conversions package:

  final constant Real D2R(unit="rad/deg") = 1'1' "Degree to Radian";
  final constant Real R2D(unit="deg/rad") = 1'1' "Radian to Degree";

Edit (off-topic): By the way, with System Modeler's withoutUnit operator, there is a related way of defining pi with empty unit and without the need for asin:

final constant pi = withoutUnit(180'deg', "rad");

The way to read this definition is: pi is the numeric value of the angle 180° expressed in radians.

@HansOlsson

Copy link
Copy Markdown
Contributor

I thought the solution is to change 180 to 180'deg', or?

Not as I see it, as that paints us even more into the corner of pi carrying unit. Edit: I take that back, it would work with or without a unit on pi.

Good.

However, there is a similar but much nicer approach which is a general recipe for all sorts of unit conversion factors (and which I believe should be used extensively in the Modelica.Units.Conversions package:

  final constant Real D2R(unit="rad/deg") = 1'1' "Degree to Radian";
  final constant Real R2D(unit="deg/rad") = 1'1' "Radian to Degree";

To me that style is plain confusing.

@henrikt-ma

henrikt-ma commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor Author

To me that style is plain confusing.

Then I recommend trying to get used to it – there are nice rewards on the other side! The 1'1' should simply be understood as the unit (as in do nothing, not as in unit of measurement) scaling factor in the world of values of quantity, with the side-effect of safely converting from one unit to another.

By the way: This replaces the older idea of setting displayUnit = "1" as a way of safely entering the correct numeric value of the ratio.

@HansOlsson

Copy link
Copy Markdown
Contributor

To me that style is plain confusing.

Then I recommend trying to get used to it – there are nice rewards on the other side!

A better solution is to find a clear notation for this, that includes the actual factor.

Code is primarily intended for people reading and understanding it - and I don't see any problem with information being redundant if tools can check it; in this case having something extra indicating that 180/pi is just a unit-conversion factor (between radian and degree) is clearer than using a special notation meaning "unit-conversion factor".

@AHaumer

AHaumer commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

I think all the discussions raise from the fact that "pi" has two meanings:

  • "pi" as an angle is equivalent to 180 degree, and should bear the unit "rad"
  • "pi" could be an irrational number without being an angle - in this case it should NOT bear a unit

ok "pi" as an angle could be seen as a dimensionless ratio = circumference / (2*radius) of a circle.
The question is: Should we remove the unit "rad" from our list of units, or should we change the way tools handle this unit?

@HansOlsson

HansOlsson commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

I think all the discussions raise from the fact that "pi" has two meanings:

  • "pi" as an angle is equivalent to 180 degree, and should bear the unit "rad"
  • "pi" could be an irrational number without being an angle - in this case it should NOT bear a unit

ok "pi" as an angle could be seen as a dimensionless ratio = circumference / (2*radius) of a circle. The question is: Should we remove the unit "rad" from our list of units, or should we change the way tools handle this unit?

I agree that either we effectively remove "rad" from the list of units or handle it; but as I will explain the unit of pi can be "rad" regardless.

Some background:

  • The problems here occur because of the idea that in multiplication literals have unit "1" (unless using unitful literals); and many electrical parameters are currently computed using multiplication involving pi - I would need to better understand the logic for that (** below).
  • The radian is equivalent to unit "1" in SI, but it is still part of the SI-standard for planar angles; https://www.bipm.org/documents/d/guest/si-brochure-9-en-pdf
  • Handling "rad" vs "1" in tools requires some care (they are compatible - but not the same). And that is something some of us have been doing for years; see https://ecp.ep.liu.se/index.php/modelica/article/view/1287 https://modelica.org/events/conference2008/sessions/session1a2.pdf (See also "*": below)
  • The normal way of computing pi in Modelica would be something like 2*Modelica.Math.asin(1.0). The output of asin has type Angle, i.e., unit "rad", and similarly if we compute sin(pi/3) we assume the input to sin has unit "rad"
  • For rotations we need to separate angular frequency (rad/s) and normal frequency (Hz, 1/s).
  • Some purist might insist on writing $\pi$ 'rad' everywhere, but apart from being awkward it is not syntactically supported for unitful literals in Modelica.

So, for tools there are two approaches:

  1. Treat "rad" exactly the same as unit "1", in that case it doesn't matter that pi has unit "rad" as it is seen as the same as unit "1". However, that approach has issues with separating angular frequency from normal frequency etc.
  2. Treat "rad" as close to, but slightly different from "1" (as Dymola already does - but could do better in some rare cases). That only works smoothly if pi has unit "rad"

So, regardless of the approach pi can have unit "rad", and then either treat it exactly as unit "1" or have a more advanced handling.

Basically, keeping "rad" as a separate unit from "1", and viewing pi as having unit "1" doesn't make sense. Basically every time you write sin(pi/3) or sin(2*pi*f*time) etc you would then add a unit "rad" in an awkward way.

* The issue with circumference is similar for any angle, if you have an angle phi (expressed in radians) then the length of an arc with that angle is phi*radius - so unit "rad.m" - which has to be treated as the same "m"; there's nothing special in terms of unit if the angle is 2*pi; it's just that it is a bit more common. But that's something we have dealt with for years.

**: For the electrical parameters it seems as if many parameter-computations involve converting between resistance, inductance, and capacitance using a factor of 100*pi (50 'Hz'). I can understand the logic, but is there a name for this?

@AHaumer

AHaumer commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

**: For the electrical parameters it seems as if many parameter-computations involve converting between resistance, inductance, and capacitance using a factor of 100*pi (50 'Hz'). I can understand the logic, but is there a name for this?

@HansOlsson 100pi is bad style. It should be written as:
f = 50 'Hz';
w = 2
pif; // 1/s or rad/s
X = w
L; // H/s = Ohm

@HansOlsson

Copy link
Copy Markdown
Contributor

**: For the electrical parameters it seems as if many parameter-computations involve converting between resistance, inductance, and capacitance using a factor of 100*pi (50 'Hz'). I can understand the logic, but is there a name for this?

@HansOlsson 100_pi is bad style. It should be written as: f = 50 'Hz'; w = 2_pi_f; // 1/s or rad/s X = w_L; // H/s = Ohm

I agree that it is bad style - so the question was how to fix it in e.g.:

Modelica.Blocks.Sources.Constant const2(k=1/100/Modelica.Constants.pi) annotation (Placement(transformation(extent={{70,70},{50,90}})));
Modelica.Blocks.Sources.Constant const3(k=1/100/Modelica.Constants.pi) annotation (Placement(transformation(extent={{100,50},{80,70}})));

I don't know whether this type of conversion is common outside of MSL and has a name, but I do see a number of cases with 2*pi in the denominator for electrical parameters.

There's something similar, but more complicated in: Modelica.Electrical.Analog.Examples.ParallelResonance

Basic.Inductor inductor1(i(fixed=true), L=0.1/(2*pi)) annotation (Placement(
transformation(
extent={{-10,-10},{10,10}},
rotation=270,
origin={-10,40})));
Basic.Capacitor capacitor1(v(fixed=true), C=0.001/(2*pi)) annotation (
Placement(transformation(
extent={{-10,-10},{10,10}},
rotation=270,
origin={10,40})));

There is a comment saying that the resonance frequency is 100'Hz', which is based on L*C*(2*pi*f)^2=1, but to me that should be documented better, either to compute the frequency based on the parameter values or the parameter values based on the resonance frequency.

@AHaumer

AHaumer commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

@HansOlsson you're right, both are bad style (my fault!) and can be written much clearer as follows.

ModelicaTest.Electrical.SinglePhase.SerialConnection:

import Modelica.Units.SI;
import Modelica.Constants.pi;
parameter SI.Frequency fNom=50;
parameter SI.Admittance Y=1;
parameter SI.Capacitance C=Y/(2*pi*fNom);
parameter SI.Reactance X=1;
parameter SI.Inductance L=X/(2*pi*fNom);
Modelica.Blocks.Sources.Constant const2(k=C);
Modelica.Blocks.Sources.Constant const3(k=L);

Modelica.Electrical.QuasiStatic.SinglePhase.ParallelResonance (same for SeriesResonance):

import Modelica.Units.SI;
import Modelica.Constants.pi;
// 2*pi*fRes = 1/sqrt(L*C);
parameter SI.Frequency fRes=1 "Resonant frequency";
parameter SI.Reactance X=1;
parameter SI.Inductance L=X/(2*pi*fRes);
parameter SI.Admittance Y=1;
parameter SI.Capacitance C=Y/(2*pi*fRes);
Modelica.Electrical.QuasiStatic.SinglePhase.Basic.Inductor inductor(L=L);
Modelica.Electrical.QuasiStatic.SinglePhase.Basic.Capacitor capacitor(C=C);

Should I provide a PR? But I'm afraid that there are lots of similar "bad style" examples around.

@HansOlsson

Copy link
Copy Markdown
Contributor

@HansOlsson you're right, both are bad style (my fault!) and can be written much clearer as follows.

ModelicaTest.Electrical.SinglePhase.SerialConnection:

import Modelica.Units.SI; import Modelica.Constants.pi; parameter SI.Frequency fNom=50; parameter SI.Admittance Y=1; parameter SI.Capacitance C=Y/(2*pi*fNom); parameter SI.Reactance X=1; parameter SI.Inductance L=X/(2*pi*fNom); Modelica.Blocks.Sources.Constant const2(k=C); Modelica.Blocks.Sources.Constant const3(k=L);

Modelica.Electrical.QuasiStatic.SinglePhase.ParallelResonance (same for SeriesResonance):

import Modelica.Units.SI; import Modelica.Constants.pi; // 2*pi*fRes = 1/sqrt(L*C); parameter SI.Frequency fRes=1 "Resonant frequency"; parameter SI.Reactance X=1; parameter SI.Inductance L=X/(2*pi*fRes); parameter SI.Admittance Y=1; parameter SI.Capacitance C=Y/(2*pi*fRes); Modelica.Electrical.QuasiStatic.SinglePhase.Basic.Inductor inductor(L=L); Modelica.Electrical.QuasiStatic.SinglePhase.Basic.Capacitor capacitor(C=C);

Should I provide a PR? But I'm afraid that there are lots of similar "bad style" examples around.

I don't think we need a new PR, but it seems like a better alternative (with more descriptive names) than the changes suggested in #4817

@henrikt-ma

Copy link
Copy Markdown
Contributor Author

I don't think we need a new PR, but it seems like a better alternative (with more descriptive names) than the changes suggested in #4817

I think this part of the conversation has drifted too far from the topic of this PR; whether pi has empty unit, "1" or "rad" does not change we one would write these expressions. Please continue this thread in #4817.

@henrikt-ma

henrikt-ma commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

So, for tools there are two approaches:

  1. Treat "rad" exactly the same as unit "1", in that case it doesn't matter that pi has unit "rad" as it is seen as the same as unit "1". However, that approach has issues with separating angular frequency from normal frequency etc.
  2. Treat "rad" as close to, but slightly different from "1" (as Dymola already does - but could do better in some rare cases). That only works smoothly if pi has unit "rad"

So, regardless of the approach pi can have unit "rad", and then either treat it exactly as unit "1" or have a more advanced handling.

System Modeler recognizes "rad" as a unit symbol, and in that sense it is not "exactly the same as unit "1"", in the same way as "J" is not exactly the same as unit "N.m" – the units are equivalent, but not the same. The key to our approach is to be gentle with the unit expressions when working out inferred units, meaning that a "rad" factor doesn't just randomly appear or disappear.

I suppose that what we do is not exactly the same as what Dymola does, but I think that System Modeler and Dymola together provide enough evidence that recognizing "rad" as a unit symbol is not something the specification should withdraw.

From my standpoint, however, this is somewhat orthogonal to what this PR is aiming for, namely that pi and e should carry no units at all so that they can participate in purely numeric – empty unit – computations. Done this way, we completely avoid the question whether the unit should be "1" or "rad", and among the good things it brings, the inferred unit for area below becomes "m2", not "rad.m2":

Real radius = 5'm';
Real area = pi * radius^2;

With an empty unit radius, an empty unit pi allows area to stay in the empty unit domain, instead of being thrown into the unitful domain with the unexpected unit "rad" for something representing area.

@AHaumer

AHaumer commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

I'm really unhappy with that discussions.
For ages I told my students: Be careful about units of angles and angular velocities.
And yes, the unit "rad" is equivalent to "1".
See an example from my field of expertise (electrical engineering): a rectified sine voltage.
grafik
model Rectifier
import Modelica.Units.SI;
import Modelica.Constants.pi;
parameter SI.Voltage Vrms=10 "RMS of voltage";
// here pi is dimensionless
parameter SI.Voltage Vmean=sqrt(2)*Vrms*2/pi "Mean of rectified sine voltage";
parameter SI.Frequency f=50 "Frequency of voltage";
parameter SI.Time T=0.5/f "Half of period";
SI.Voltage v "Sine voltage";
SI.Voltage vRect "Rectified sine voltage";
SI.MagneticFlux x(start=0,fixed=true) "Integral to obtain mean";
discrete SI.Voltage vMean(start=Vmean, fixed=true) "Mean of rectified sine voltage";
equation
// here pi is an angle
v=sqrt(2)*Vrms*sin(2*pi*f*time);
vRect=abs(v);
der(x)=vRect;
when sample(T, T) then
vMean=x/T;
reinit(x, 0);
end when;
annotation (experiment(StopTime=0.2, Interval=0.0005, Tolerance=1e-05));
end Rectifier;
IMHO everything is consistent, and we see pi as "rad" and pi as "1".

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants