modelica / ModelicaStandardLibrary

Free (standard conforming) library to model mechanical (1D/3D), electrical (analog, digital, machines), magnetic, thermal, fluid, control systems and hierarchical state machines. Also numerical functions and functions for strings, files and streams are included.
https://doc.modelica.org
BSD 3-Clause "New" or "Revised" License
481 stars 169 forks source link

Modelica.Constants.Inf should be consistent with the Modelica Language Specification #4503

Open casella opened 4 days ago

casella commented 4 days ago

Follow-up of #4479.

I scanned the current MLS 3.6 for "inf" (relative to Real variables) and I found two places where it is mentioned. The first is the definition of the Real type, Section 4.8.1:

 type Real // Note: Defined with Modelica syntax although predefined
  RealType ⟨value⟩; // Not an attribute; only accessed without dot-notation
  parameter StringType quantity    = "";
  parameter StringType unit        = "" "Unit used in equations";
  parameter StringType displayUnit = "" "Default display unit";
  parameter RealType min = -Inf, max = +Inf; // Inf denotes a large value
  parameter RealType start;            // Initial value
  parameter BooleanType fixed = true,  // default for parameter/constant;
                              = false; // default for other variables
  parameter RealType nominal;            // Nominal value
  parameter BooleanType unbounded = false; // For error control
  parameter StateSelect stateSelect = StateSelect.default;
equation
  assert(min <= ⟨value⟩ and ⟨value⟩ <= max, "Variable value out of limit");
end Real; 

In the definition of the defaults for RealType.min and RealType.max, Inf "denotes a large value". This definition is a bit vague, but one could reasonably argue that this definition is some kind of meta-Modelica or pseudo-Modelica code, since the Real type is built-in, so a compiler developer could interpret the assert statement in the following way

if max is left to the default, don't check⟨value⟩ <= max if min is left to the default, don't check ⟨value⟩ >= min

which is, BTW, the most efficient way to handle that. So, there is really no need to have a numerical representation of Inf to support the definition of Real type. In fact, the elephant in the room here is that there is actually no definition of what RealType actually means. But that is not an issue for MAP-Lib to discuss.

The second is in Section 10.3.4.1 (reduction expressions). The caption for Table 10.3 reads:

Table 10.3: Reduction expressions with iterators. (The least and greatest values of Real are available as -Modelica.Constants.inf and Modelica.Constants.inf, respectively.)

In this case, the MLS mentions explicitly -Modelica.Constants.inf and Modelica.Constants.inf as the least and greatest value of Real (whatever Real means). So, unless we change the MLS, it is not entirely up to MAP-Lib to decide what these constants actually mean - they should be consistent with this definition. Note the interesting similarity (but not the identity) between the default min and max attributes of Real and the default value for empty min and max reductions.

Now, consider the following MWE, describing the deformation of a MEMS device, that has a certain stiffness k within certain bounds, but becomes s times stiffer outside those bounds. The model is implicit and requires some iterations to figure out the displacement, given the force amplitude.

model MWE
  Real x(nominal = 1e-6);
  Real F;
  parameter Real b[:] = fill(0.0, 0) "Array of bounds for x; leave empty for no bounds";
  parameter Real s = 100 "Stiffness parameter";
  parameter Real k = 1e-5 "Linear stiffness";
  parameter Real F_0 = 1e-4 "Force amplitude";
  parameter Real omega = 1e4 "Force angular frequency";
equation
  F = F_0*sin(omega*time);
  F = k*(if x > max(b) then max(b)+ s*(x - max(b))
            else if x < min(b) then min(b) + s*(x - min(b))
            else x);
end MWE;

Suppose now that the tool is using the nominal attribute for scaling purposes, as suggested by MLS 4.8.1, so it generates two scaled zero-crossing functions

zc1 = (x - max(b))/x.nominal
zc2 = (min(b) - x)/x.nominal

for improved precision in determining the exact point in time when the event should be triggered.

With the previous definition Modelica.Constant.inf = 1e60 this would work fine, assuming the code is eventually compiled into double-precision IEEE 754-compliant executable code. With the new definition of Modelica.Constant.inf = 1.7976931348623157E+308 there would be overflow and the behaviour would be undefined.

However, the new definition of Modelica.Constants.inf in #4042 is "Maximum representable finite floating-point number", which is finally assigned to ModelicaServices.Machine.inf also defined as "Maximum representable finite floating-point number". The actual value provided by ModelicaServices.Machine.inf is by definition "machine-dependent", where I understand that "machine" means the combination of the tool(s) that generate(s) the code and the hw architecture on which it runs. There is no explicit reference to IEEE 745 MAXVAL in these definitions.

So, when using Modelica.Constants.inf, a user of the MSL should not assume that it will get any specific numerical value. That will be an internal decision of the tool, based on whatever code generation methods and target architectures are selected. The only expectation of a MSL user is that eventually the tool should handle that value properly in all meaningful contexts.

Do I understand this correctly and do we have an agreement on that?

@HansOlsson, @dzimmer, @henrikt-ma

HansOlsson commented 4 days ago

No, that only indicates that tools shouldn't use nominal in such a bad way.

The goal with nominal is to get values closer to "1" - if that introduces an overflow, it's clearly not working and the code should be rewritten - without updating the models.

henrikt-ma commented 4 days ago

I largely agree.

Regarding the MWE, I would say that it is the users responsibility to think carefully about how the model should work when there are no bounds. In this case, I think there is a fundamental mistake in the equation which does not have to do with exactly how big "a very large number" is: when there are no bounds one wants to always end up in the F = k * x case, but what happens here is completely different due to the often surprising fact that we can have max(b) < min(b). The "trick" of writing x > max(b) to compare against the optional upper bound is simply no good.

I also agree that it is not good that the specification speaks of some pseudo code Inf in one place, and Modelica.Constants.inf in another. The two should be related to each other, so that it is clear that the default requirement on a RealType value x is that both x and -x shall be representable on the machine.

HansOlsson commented 4 days ago

Oh, I didn't realize that max(b) with an empty array was used for bounds in that way, I agree that it is very bad style and I will try to unsee it.

I think that the reason the specification used both pseudo code used Inf and Modelica.Constant.inf is left-over from the time that Modelica.Constant.inf was just sort of a placeholder. Now that it has been corrected we can use the same definition or value in both places.

henrikt-ma commented 4 days ago

I would rather go in the other direction and make the MLS more self-contained instead of becoming even more dependent upon the MSL.

HansOlsson commented 4 days ago

I would rather go in the other direction and make the MLS more self-contained instead of becoming even more dependent upon the MSL.

I see that one of the options I gave: Use the same definition for both, i.e., sort of "inline the Modelica.Constants.inf" definition in the specification in those two places (or once and referring to the other). That makes it consistent without having a dependency.

henrikt-ma commented 4 days ago

Good. If we do that, then it might also be a good idea to relate the Modelica.Constants.inf to the default max of RealType in the specification, to keep the connection but in the other direction.