modelica / ModelicaSpecification

Specification of the Modelica Language
https://specification.modelica.org
Creative Commons Attribution Share Alike 4.0 International
101 stars 41 forks source link

What does 'partial' actually mean? #3598

Open casella opened 3 days ago

casella commented 3 days ago

The definition of partial classes in the MLS is a bit vague, see Section 4.4.2:

A class defined with partial in the class-prefixes is called a partial class. Such a class is allowed to be incomplete, and cannot be instantiated in a simulation model; useful, e.g., as a base class.

as it is not really clear what "incomplete" means. I always understood this meant: variables/parameters/connectors/classes are defined, but equations/binding equations (i.e. actual behaviour) may be missing, because the partial model aims at being generic, and are meant to be provided when extending the model. I'm not sure where exactly I got this idea, but this is not really clear in the text of the specification.

I made a simple MWE:

package TestPartial
  partial model A
    Real x = p;
  end A;

  model B
    extends A;
    parameter Real p = 10;
  end B;

  partial model C
  annotation(
      Icon(graphics = {Ellipse(origin = {0, 0}, extent = {{-p, p}, {p, -p}})}));
  end C;

  model D
    extends C;
    parameter Real p = 10;
  end D;

  partial model E
  annotation(
      Icon(graphics = {Ellipse(origin = {0, 0}, 
                               extent = DynamicSelect({{- 10, 10}, {10, -10}},{{-r, r}, {r, -r}}))}));  
  end E;

  model F
    extends E;
    Real r = 10*(1 + 5*time);
  end F;
end TestPartial;

To me, there is no question that A should not be valid: a binding equation using an undefined component p that may be defined later seems quite fishy to me. As a consequence, B should also be invalid, since it would be weird that extending an invalid model could make it valid. Both Dymola and OpenModelica agree with this interpretation. The same should not only hold for binding equations, but also for equations in equations and initial equation sections, and for algorithm in algorithm and initial algorithm sections. If there is agreement on this, I think this should be made more explicit in the text of the specification.

The question is, what about models C to E, which use undefined components inside annotations, not equations, binding equations, or algorithms?. At the moment, Dymola and OpenModelica accept them, though OpenModelica complains about undefined stuff when rendering C and E in the GUI.

My preference would be to be consistent and use the same criteria for equations, binding equations, algorithms, and annotations. On the other hand, if existing Modelica tools accept these models as (kind of) valid, this may break existing libraries that otherwise work fine, so I would also be fine to relax the requirement for annotations, with the idea that you don't really check them in partial classes, but only in concrete classes which are not partial. In any case, this should be made clear in the specification, so that people can actually write portable models.

Adding @perost, @dietmarw to the discussion, since they began it in OpenModelica/OpenModelica#13092.

HansOlsson commented 3 days ago

Dymola 2025x (and some earlier versions) also complains about this partial model when:

In MSL it was already handled in: https://github.com/modelica/ModelicaStandardLibrary/pull/4147 (at least one DynamicSelect, but most for enable).

In practice it is often unrelated to partial and the annotations are just left-overs from previous iterations of the models.

maltelenz commented 2 days ago

My preference would be to be consistent and use the same criteria for equations, binding equations, algorithms, and annotations.

This seems like the sensible approach to me as well.

henrikt-ma commented 2 days ago

For a start, could we agree on adding something along the lines of this?

It is an error if the partial class cannot be extended in a way that makes the extending class a valid non-partial class.

I hope that the Modelica lookup rules are such that the rejection of B does not depend upon whether A is partial or not. With the rule above, I think it would then be safe to simply reject A with a lookup error regardless of its partial status.

With this approach, the question about the annotations would boil down to a question of lookup rules within annotations.

HansOlsson commented 2 days ago

For a start, could we agree on adding something along the lines of this?

It is an error if the partial class cannot be extended in a way that makes the extending class a valid non-partial class.

To me that formulation is somewhat problematic, as that allows a partial class that can only be extended in one way. And for this particular case one could argue that it is in fact possible to extend it with contents that sort of makes it ok. On the other hand having a rule that it can be extend in every possible way seems complicated as well.

I hope that the Modelica lookup rules are such that the rejection of B does not depend upon whether A is partial or not. With the rule above, I think it would then be safe to simply reject A with a lookup error regardless of its partial status.

Agreed, and lookup is the same regardless of partial. I don't think that needs stating.

With this approach, the question about the annotations would boil down to a question of lookup rules within annotations.

Obviously they should use the same lookup-rule when applicable, it depends on whether the annotations are intended to be values or something else, and for choices-annotations we already have a recommendation to use globally accessible names - but the general rule is that they are using the normal lookup - https://specification.modelica.org/master/inheritance-modification-and-redeclaration.html#annotation-choices-for-suggested-redeclarations-and-modifications

I think it helps to get the discussion on track by understanding what the specification currently says in https://specification.modelica.org/master/class-predefined-types-and-declarations.html#component-declaration-static-semantics

A class defined with partial in the class-prefixes is called a partial class. Such a class is allowed to be incomplete, and cannot be instantiated in a simulation model; useful, e.g., as a base class. See ... regarding short class definition semantics of propagating partial.

It doesn't say that any rules are changed, only that it may be "incomplete".

The clarification would be to state that "incomplete" means missing equations and/or algorithms, specifically:

(I can't recall if there's anything more, and I don't know if missing variables would also be possible - i.e., a partial model lacking variables.)

HansOlsson commented 2 days ago

After some thinking I realized that it seems possible to have too many equations in a partial model:

partial block InverseBlockConstraintsLeft
  "Construct inverse model by requiring that two inputs and two outputs are identical"

  Modelica.Blocks.Interfaces.RealInput u1 "Input signal 1 (u1 = u2)"
    annotation (Placement(transformation(extent={{-240,-20},{-200,20}}), iconTransformation(extent={{-240,-20},{-200,20}})));
  Modelica.Blocks.Interfaces.RealInput u2 "Input signal 2 (u1 = u2)"
    annotation (Placement(transformation(extent={{-140,-20},{-180,20}}), iconTransformation(extent={{-140,-20},{-180,20}})));
equation 
  u1 = u2;
  annotation (
    defaultConnectionStructurallyInconsistent=true);
end InverseBlockConstraintsLeft;

I don't know why someone would split that model (Modelica.Blocks.Examples.InverseModel) that way but at least it seems possible. So "missing equations, algorithms, and/or (in rare cases) unknowns (variables), "

henrikt-ma commented 2 days ago

The clarification would be to state that "incomplete" means missing equations and/or algorithms, specifically:

  • A partial model (or block) is not required to be balanced
  • A Real variable declared as discrete in a partial model (or block) does not need to be assigned in a when-clause
  • A partial function may lack algorithm and external clause

(I can't recall if there's anything more, and I don't know if missing variables would also be possible - i.e., a partial model lacking variables.)

The point of requiring that the partial model can be extended in some way that results in a valid non-partial model has the benefit of not requiring us to write down a complete list of exceptions. Instead of a complete list, we could just give some examples:

It is an error if the partial class cannot be extended in any way that makes the extending class a valid non-partial class. [Examples of conditions that cannot be checked for a local class:

  • A partial model does not need to be locally balanced, since local balance can be achieved in an extending model that adds more variables or equations.
  • A partial function is not required to assign all output variables, since an extending function can deal with that using binding equations or by providing an algorithm or external clause (in case the partial function did not have one). ]
perost commented 2 days ago

The point of requiring that the partial model can be extended in some way that results in a valid non-partial model has the benefit of not requiring us to write down a complete list of exceptions. Instead of a complete list, we could just give some examples:

It is an error if the partial class cannot be extended in any way that makes the extending class a valid non-partial class. [Examples of conditions that cannot be checked for a local class:

  • A partial model does not need to be locally balanced, since local balance can be achieved in an extending model that adds more variables or equations.
  • A partial function is not required to assign all output variables, since an extending function can deal with that using binding equations or by providing an algorithm or external clause (in case the partial function did not have one). ]

One issue with this is that the rule says that there are cases that would make a partial class unusable, but the examples give no hints on what these cases might be. To me the examples make more sense with the original wording that partial models may be incomplete.

HansOlsson commented 2 days ago

The clarification would be to state that "incomplete" means missing equations and/or algorithms, specifically:

  • A partial model (or block) is not required to be balanced
  • A Real variable declared as discrete in a partial model (or block) does not need to be assigned in a when-clause
  • A partial function may lack algorithm and external clause

(I can't recall if there's anything more, and I don't know if missing variables would also be possible - i.e., a partial model lacking variables.)

The point of requiring that the partial model can be extended in some way that results in a valid non-partial model has the benefit of not requiring us to write down a complete list of exceptions. Instead of a complete list, we could just give some examples:

Ok, but to me this is clearly separate from the issue of lookup in annotations; especially considering that extending with a class also containing a new variable should not influence the lookup in the partial model.

It is an error if the partial class cannot be extended in any way that makes the extending class a valid non-partial class. [Examples of conditions that cannot be checked for a local class:

  • A partial model does not need to be locally balanced, since local balance can be achieved in an extending model that adds more variables or equations.
  • A partial function is not required to assign all output variables, since an extending function can deal with that using binding equations or by providing an algorithm or external clause (in case the partial function did not have one). ]

My concerns with adding an explicit rule for this are:

To me it makes more sense to just be clearer that annotations using values use the same lookup as other things.

Consider:

partial connector C Real x; end C;
partial model M
  C c1;
end M;  

This would define an interface as a model with a connector c1 containing a real variable x, but you cannot use it as a base-class. I don't know if it is meaningful, but I don't see a problem with such a class existing in Modelica.