ModelicaSpecification icon indicating copy to clipboard operation
ModelicaSpecification copied to clipboard

What happens to modifiers appearing after a redeclare?

Open qlambert-pro opened this issue 1 year ago • 14 comments

What is the value of a.x, in the following example?

model Test
  model A0
    Real x = 1;
  end A0;

  model A1
    extends A0;
    Real y = 2;
  end A1;

  model B0
    replaceable A0 a;
  end B0;

  model B1
    extends B0(a.x = 3);
  end B1;

  extends B1(redeclare A1 a);
end Test;

qlambert-pro avatar Feb 08 '23 13:02 qlambert-pro

a.x is 3

It's a bit hidden - https://specification.modelica.org/master/inheritance-modification-and-redeclaration.html#constraining-type says

for B1

In an element modification of a replaceable element, the modifications are applied both to the actual type and to the constraining type.

And then for extends B1:

In an element-redeclaration of a replaceable element the modifiers of the replaced constraining type are merged to both the new declaration and to the new constraining type, using the normal rules where outer modifiers override inner modifiers.

HansOlsson avatar Feb 08 '23 13:02 HansOlsson

Does that make the following model invalid, because there is no k in the actual redeclared model. Or are we supposed to just drop modifications from intervening redeclarations?

model Test7
  model M0
  end M0;

  model M1
    extends M0;
    parameter Real k = 1;
  end M1;

  model B
    replaceable M0 m;
  end B;

  model A
    extends B(redeclare replaceable M1 m(k = 1));
  end A;

  extends A(redeclare M0 m);
end Test7;

qlambert-pro avatar Feb 10 '23 15:02 qlambert-pro

Drop modifications from intervening redeclarations, unless they modify the constraining type. So, if you want to make the example illegal you have the modifier as a non-redeclaration:

model Test8
  model M0
  end M0;

  model M1
    extends M0;
    parameter Real k = 1;
  end M1;

  model B
    replaceable M0 m;
  end B;

  model C
    extends B(redeclare replaceable M1 m);
  end C;

  model A
    extends C(m(k=1));
  end A;

  extends A(redeclare M0 m);
end Test8;

or modify the constraining type:

model Test9
  model M0
  end M0;

  model M1
    extends M0;
    parameter Real k = 1;
  end M1;

  model B
    replaceable M0 m;
  end B;

  model A
    extends B(redeclare replaceable M1 m(k = 1) constrainedby M1(k=1));
  end A;

  extends A(redeclare M0 m);
end Test9;

HansOlsson avatar Feb 10 '23 15:02 HansOlsson

I guess in this example k=1 and k.start = 3

model Test15
  model M0
  end M0;

  model M1
    extends M0;
    replaceable parameter Real k = 1;
  end M1;

  model B
    replaceable M0 m;
  end B;

  model A
    extends B(replaceable M1 m(replaceable Real k = 5 constrainedby Real(start = 3))
              constrainedby M1(replaceable Real k = 2 constrainedby Real(start = 4)));
  end A;

  extends A(m (redeclare Real k));
end Test15;

qlambert-pro avatar Feb 17 '23 10:02 qlambert-pro

After revisiting this issue I now think that in Test5, k=5 and k.start = 3

qlambert-pro avatar Feb 15 '24 12:02 qlambert-pro

After revisiting this issue I now think that in Test5, k=5 and k.start = 3

I believe that this is the correct result if the last line before end Test15 was just extends A;

With extends A(m (redeclare Real k));, redeclaration of m.k requires that we drop intervening modification that come with redeclarations. This aspect, by the way, is not very clear, as I mentioned in the comment in #3474. More on that later. So we definitely need to drop k = 5. But we need to keep the modifications that come on the constrainedby-clauses. The question is which constrainedby-clause is the active one. I believe that because the last redeclare is of the m.k and not of M1, we need to grab constrainedby that came with the latest redeclare of m.k, which is replaceable M1 m(replaceable Real k = 5 constrainedby Real(start = 3). So, m.k=1 and m.k.start = 3.

The rule that modifications on intervening redeclarations in not spelled out and also can be interpreted differently. See @HansOlsson's example of Test8. My first impression was that the modification that comes from:

  model A
    extends C(m(k=1));
  end A;

would need to be dropped because it is a modification on an intervening redeclaration. However, it appears that any non-redeclare modifications accumulate, and any intermediate redeclare modifications are discarded. It works naturally from the implementation point of view. When we merge the modifications from extends A(redeclare M0 m); with the modifications given in A, we have no idea that there will be any redeclarations coming. By the time we get to the redeclaration in C, we don't know that m(k=1) was an intermediate modification.

So, I am in favor of making that point more clear in the specification.

eshmoylova avatar Mar 15 '24 20:03 eshmoylova

Yes, I agree with you. This is how WSM will behave in the next release.

qlambert-pro avatar Mar 18 '24 05:03 qlambert-pro

I have looking through various examples of redeclarations and modifications, and I found a model that I thought should fail but works in 3 different tools that I tried. This is a variant of Test7 but the difference is that the first redeclare in model A does not include replaceable. I also modified the model to have a variable, so that there is something in the simulation results.

model Test7A
  model M0
    Real x=1;
  end M0;

  model M1
    extends M0(x=k);
    parameter Real k = 1;
  end M1;

  model M2
    extends M0(x=2*k);
    parameter Real k = 3;
  end M2;

  model B
    replaceable M0 m;
  end B;

  model A
    extends B(redeclare M1 m(k = 1));
  end A;

  extends A(redeclare M2 m);

end Test7A;

First question: is it allowed to redeclare m when extending A? I tried in 3 different tools and none complained. But Restrictions on Redeclarations state:

Only classes and components declared as replaceable can be redeclared with a new type, which must have an interface compatible with the constraining interface of the original declaration, and to allow further redeclarations one must use redeclare replaceable.

Second question: what should the result be? And should it be the same as if there was replaceable in the redeclaration in model A as below?

  model A
    extends B(redeclare replaceable M1 m(k = 1));
  end A;

My expectation based on the discussion in this ticket is that, if allowed, x = 6. This is the result I am getting in MapleSim (development version) and OpenModelica. But in Dymola the result is different depending on whether there is replaceable or not. My version of Dymola is rather out of date. @HansOlsson are there more nuances to

Drop modifications from intervening redeclarations, unless they modify the constraining type.

that need to be clarified?

eshmoylova avatar Apr 01 '24 14:04 eshmoylova

First question: is it allowed to redeclare m when extending A?

I consider it a bug in System Modeler that we are not complaining about the missing replaceable.

Second question: what should the result be? And should it be the same as if there was replaceable in the redeclaration in model A as below?

I would expect x=6 too.

qlambert-pro avatar Apr 02 '24 08:04 qlambert-pro

I have looking through various examples of redeclarations and modifications, and I found a model that I thought should fail but works in 3 different tools that I tried. This is a variant of Test7 but the difference is that the first redeclare in model A does not include replaceable. I also modified the model to have a variable, so that there is something in the simulation results.

model Test7A
  model M0
    Real x=1;
  end M0;

  model M1
    extends M0(x=k);
    parameter Real k = 1;
  end M1;

  model M2
    extends M0(x=2*k);
    parameter Real k = 3;
  end M2;

  model B
    replaceable M0 m;
  end B;

  model A
    extends B(redeclare M1 m(k = 1));
  end A;

  extends A(redeclare M2 m);

end Test7A;

First question: is it allowed to redeclare m when extending A? I tried in 3 different tools and none complained. But Restrictions on Redeclarations state:

Only classes and components declared as replaceable can be redeclared with a new type, which must have an interface compatible with the constraining interface of the original declaration, and to allow further redeclarations one must use redeclare replaceable.

The problem here is what does 'redeclare with the same type mean', especially as Modelica normally does not use a nominal type system making it hard to know the meaning of "the same type". The best would be if it would be possible to avoid such redeclarations - and have another mechanism for them.

The minimum would be to deprecate them when not needed, so only allow if redeclaration with the same type if:

  • Variability is included
  • Array dimensions are included

However, for the next item it is easier to consider if we have "the same type":

model A
    extends B(redeclare M2 m(k = 1));
 end A;

 extends A(redeclare M2 m);

Second question: what should the result be? And should it be the same as if there was replaceable in the redeclaration in model A as below?

  model A
    extends B(redeclare replaceable M1 m(k = 1));
  end A;

My expectation based on the discussion in this ticket is that, if allowed, x = 6. This is the result I am getting in MapleSim (development version) and OpenModelica. But in Dymola the result is different depending on whether there is replaceable or not. My version of Dymola is rather out of date. @HansOlsson are there more nuances to

Drop modifications from intervening redeclarations, unless they modify the constraining type.

that need to be clarified?

If replaceable and original case it seems clear that it should be x=6 and k=3, and even if both declarations used M2 one would expect k=3.

Without replaceable it becomes messy: a non-replaceable redeclare should can be expected to work as normal declaration, and if we had a declaration 'M2 m(k=1)' one wouldn't expect a redeclare to remove the 'k=1' modifier, so one would expect k=1.

HansOlsson avatar Apr 02 '24 09:04 HansOlsson

Oh right I keep forgetting that redeclaring non-replaceable element is allowed...

qlambert-pro avatar Apr 02 '24 09:04 qlambert-pro

I had gone through the specification before posting Test7A trying to find validation why it would not complain about replacing non-replaceable with a different type. But I cannot find a justification for it backed by the specification. To be sure, I had also tried but not posted Test7B which is the same as Test7A but define M2 to be interface non-compatible by changing k to p:

  model M2
    extends M0(x=2*p);
    parameter Real p = 3;
  end M2;

Dymola gives an error that there is no k when it tries to apply the modification. OpenModelica removes the modification on intermediate redeclaration, so it does not give an error and m.x = 6. MapleSim released gives the error about the modification but the development version does what OpenModelica does (blame a very confused developer). But none is complaining that we are redeclaring non-replaceable with a different type.

The question is: is it a (multi-)tool bug or is that a feature that needs to be properly documented?

Letting the redeclaration happen without the presence of replaceable and, if the type is not compatible, there will be an error later is a reasonable way to implement the restriction. But how the modifications should be merged is not clear at all. From @HansOlsson's response, I am getting that if we have redeclare (2) on redeclare replaceable (1) the modification that came with redeclare (1) should be dropped (unless on the constrained type). But if we have redeclare (2) on redeclare (1), we need to merge them following the regular rules that modifications of (2) would be outer and those of (1) would be inner.

@gkurzbach do you have an opinion on redeclaring non-replaceable and how to merge modifications in that case (see Test7A in comments above)?

eshmoylova avatar Apr 04 '24 01:04 eshmoylova

The justification is in Section 7.3.3:

Only classes and components declared as replaceable can be redeclared with a new type, which must have an interface compatible with the constraining interface of the original declaration, and to allow further redeclarations one must use redeclare replaceable. [Redeclaration with the same type can be used to restrict variability and/or change array dimen- sions.]

and

Array dimensions may be redeclared; provided the sub-typing rules in section 6.4 are satisfied. [This is one example of redeclaration of non-replaceable elements.]

qlambert-pro avatar Apr 04 '24 06:04 qlambert-pro

@gkurzbach do you have an opinion on redeclaring non-replaceable and how to merge modifications in that case (see Test7A in comments above)?

I think the whether replaceable exist or not should not change the basic behavior. The existence should be just an allowance to redeclare more, not just the dimension.

SimulationX itself is very sloppy with error messages. It performs the redeclaration whether or not replacable is present.

In case of Test7B, my interpretation is as follows: When redeclare something the original declaration is used to check against, not the intemediate redeclaration. It is replaceable M0 m and it contains only x. So it is not a problem to redeclare it with the modified M2. See 7.3.2 Contraining type: If the constraining-clause is not present in the original declaration (i.e., the non-redeclared declaration): The type of the declaration is also used as a constraining type. and In a redeclaration of a replaceable element, the class or type of a component must be a subtype of the constraining type.

gkurzbach avatar Apr 04 '24 08:04 gkurzbach