Skip to content

Fix unit inconsistencies caused by 'pi' carrying a unit - #4817

Open
henrikt-ma wants to merge 2 commits into
modelica:masterfrom
henrikt-ma:units-exposed-by-pi-literals
Open

henrikt-ma wants to merge 2 commits into
modelica:masterfrom
henrikt-ma:units-exposed-by-pi-literals

Conversation

@henrikt-ma

@henrikt-ma henrikt-ma commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

This PR fixes unit inconsistencies which are caused by Modelica.Constants.pi carrying a unit. That is, the presence of a unit on pi makes an entire expression such as 0.1/(2*pi) have unit, but not the consistent unit for the component it modifies.

I am personally convinced that pi should not have a unit at all (the topic of #4774), which would make this entire PR unnecessary, but fortunately the changes introduced in this PR are not relying on pi having a unit.

@henrikt-ma henrikt-ma added the requires Modelica 3.7 Issue that requires Modelica Language Specification 3.7 label Oct 2, 2026
@HansOlsson

Copy link
Copy Markdown
Contributor

Just some preliminary comments. We need to first ensures that CI/CD handling can handle the syntax.

startTime=startTime) annotation (Placement(transformation(extent={{-160,
-30},{-140,-10}})));
Modelica.Blocks.Sources.Sine sine(amplitude=200, f=50/pi)
Modelica.Blocks.Sources.Sine sine(amplitude=200, f=50'Hz'/pi)

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.

We should be careful with Hertz (just a note on this, but all need to be checked).

Despite being defined as 1/s it shouldn't be used for everything with unit 1/s according to BIPM, but only periodic signals that are something/s.

In this case it seems like a complete mess and 50/pi might be intended to be 100'rad/s'/(2*pi).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The unit of f is "Hz", so having "Hz" in its modification seems correct to me.

Comment on lines +32 to +33
Modelica.Blocks.Sources.Constant const2(k=1'F'/100/Modelica.Constants.pi) annotation (Placement(transformation(extent={{70,70},{50,90}})));
Modelica.Blocks.Sources.Constant const3(k=1'H'/100/Modelica.Constants.pi) annotation (Placement(transformation(extent={{100,50},{80,70}})));

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 believe this needs to be considered more, the 100*pi is 50'Hz'2pi, where 50 Hertz is the normal frequency in Europe.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is where the constants are used:

    connect(const2.y, variableCapacitor.C) annotation (Line(points={{49,80},{40,80},{40,42}}, color={0,0,127}));
    connect(const3.y, variableInductor.L) annotation (Line(points={{79,60},{70,60},{70,42}},   color={0,0,127}));

Here, variableCapacitor.C has unit "F", and variableInductor.L has unit "H".

That's as far as the units go. Regarding how to express the value, would you prefer this?

Suggested change
Modelica.Blocks.Sources.Constant const2(k=1'F'/100/Modelica.Constants.pi) annotation (Placement(transformation(extent={{70,70},{50,90}})));
Modelica.Blocks.Sources.Constant const3(k=1'H'/100/Modelica.Constants.pi) annotation (Placement(transformation(extent={{100,50},{80,70}})));
Modelica.Blocks.Sources.Constant const2(k=0.02'F'/(2*Modelica.Constants.pi)) annotation (Placement(transformation(extent={{70,70},{50,90}})));
Modelica.Blocks.Sources.Constant const3(k=0.02'H'/(2*Modelica.Constants.pi)) annotation (Placement(transformation(extent={{100,50},{80,70}})));

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.

That is one possibility, the other would be something like:

Suggested change
Modelica.Blocks.Sources.Constant const2(k=1'F'/100/Modelica.Constants.pi) annotation (Placement(transformation(extent={{70,70},{50,90}})));
Modelica.Blocks.Sources.Constant const3(k=1'H'/100/Modelica.Constants.pi) annotation (Placement(transformation(extent={{100,50},{80,70}})));
Modelica.Blocks.Sources.Constant const2(k=1'S'/(2*Modelica.Constants.pi*50'Hz')) annotation (Placement(transformation(extent={{70,70},{50,90}})));
Modelica.Blocks.Sources.Constant const3(k=1'Ohm'/(2*Modelica.Constants.pi*50'Hz')) annotation (Placement(transformation(extent={{100,50},{80,70}})));

I could see some explanation along those lines based on resonance circuits etc, but I don't have the domain knowledge to judge whether people normally write (and/or understand) formulas like that or not.

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.

#4774 (comment) gave a more readable solution

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So the suggestion is simply to introduce new parameters instead of the unitful literals? If this is what everyone wants, I'll do it that way. @AHaumer ?

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.

To be clear it is new parameters revealing the logic of the model, as indicated in that comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think the suggestion in "that comment" is missing some use of final. @AHaumer would you agree with this?

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

Or should some of {fNom, Y, X} also be final?

(ParallelResonance and SeriesResonance would make similar use of final, I suppose.)

rotation=270,
origin={-10,70})));
Basic.Inductor inductor1(i(fixed=true), L=0.1/(2*pi)) annotation (Placement(
Basic.Inductor inductor1(i(fixed=true), L=0.1'H'/(2*pi)) annotation (Placement(

@beutlich beutlich Oct 5, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Those changes disclose another dilemma that moparser fron deprecated repository https://github.com/modelica-tools/ModelicaSyntaxChecker is still used here while no adequate replacement is in sight or in-place. Therefore we need to address this CI issue beforehand -> #4821.

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.

See #4774 (comment) for a better alternative for this model

Comment on lines -10052 to +10054
input SI.Area A_cross(min=Modelica.Constants.eps) = PI*0.1^2/4
input SI.Area A_cross(min=Modelica.Constants.eps) = PI*(0.1'm')^2/4
"Cross sectional area" annotation (Dialog(group="Geometry"));
input SI.Length perimeter(min=Modelica.Constants.eps) = PI*0.1
input SI.Length perimeter(min=Modelica.Constants.eps) = PI*0.1'm'

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.

This also indicates that the classes aren't well-designed.

Both the cross area and perimeter have defaults based on the diameter being 0.1'm', but instead of having one default depending on the other input (or on a common diameter input), the default diameter is duplicated in this function and related ones (it can only be a default as the goal is to support non-circular cross-sections). That makes it more error-prone.

It would be possible to introduce a backwards compatible change for that by having a new trailing diameter-input that only impacts the defaults. The down-side is that existing calls wouldn't modify the default-diameter and that value would be a bit confusing.

Alternatively, it would also be possible to have the default for the cross-section depend on the perimeter (A_cross=perimeter^2/(4*PI) without that down-side. Technically it would be non-backwards compatible, but any impacted model would be problematic.

(Note that it uses the general term "perimeter", not "circumference".)

henrikt-ma and others added 2 commits October 20, 2026 22:46
The default areas and perimeters of the Fluid.Dissipation records, and of
the inputs of dp_twoPhaseMomentum_DP, are computed from a diameter written
as a plain number, such as pi*0.1^2/4 for an area and pi*0.1 for a
perimeter.  Since pi carries a unit, these defaults had the unit of pi
instead of m2 and m.  The diameters are now given as unitful literals, as
already done for the same defaults in ModelicaTest.Fluid.Dissipation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@beutlich
beutlich force-pushed the units-exposed-by-pi-literals branch from 3adcd2d to 41ddd37 Compare October 20, 2026 20:46
@AHaumer

AHaumer commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Sorry, no I won't accept what is discussed here (and in other PRs touching that topic).
pi might be an angle, having the unit "rad" equivalent to "1".
pi might be just an irrational mumber, having no unit.
Tools should develop heuristics to decide whether to use "no unit" or "rad".
I will also not accept changes in the MSL like:
Modelica.Blocks.Sources.Constant const2(k=1'S'/(2*Modelica.Constants.pi*50'Hz'));
That's horrible! Just spend 2 parameters and it looks nice:
parameter SI.Conductance Y=1;
parameter SI.Frequency f=50;
Modelica.Blocks.Sources.Constant const2(k=Y/(2*pi*f));

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

requires Modelica 3.7 Issue that requires Modelica Language Specification 3.7

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants