Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify value modification #2747

Merged
merged 6 commits into from
Apr 5, 2022
Merged

Simplify value modification #2747

merged 6 commits into from
Apr 5, 2022

Conversation

henrikt-ma
Copy link
Collaborator

This PR is a subtopic of #2558 concerning value modification. The first task to be addressed by this PR is to summarize the current state of value modifications considering various restrictions put into Flat Modelica, and identify any problems. After that, we might also be able to further simplify value modification.

@henrikt-ma henrikt-ma marked this pull request as draft December 3, 2020 08:19
@henrikt-ma henrikt-ma changed the base branch from master to MCP/0031 December 3, 2020 08:20
@henrikt-ma
Copy link
Collaborator Author

henrikt-ma commented Dec 3, 2020

There has only been minor work done on value modifications so far. The following is extracted and cleaned up from #2558 (comment).

Examples for Modifications of values

Should references in declaration equations of types be avoided?

record 'R'
  constant Real 'c' = 1.0;
  parameter Real 'pc' = 2 * 'c'; /* Will modify the default of 'pc' when 'c' is modified, adds significant complexity. */
end 'R';
record 'R'
  Real 'x' = 1.0;
  Real 'xx' = 2 * 'x'; /* Even worse as 2 * 'x' is non-constant. */
end 'R';

record 'R2' = 'R'('x' = 2);

model 'M'
  'R' r = 'R'();   /* Using the record constructor overwriting the declaration equation is rather easy to handle. */
  'R' 'r2' = 'R2'();   /* Call record constructor of 'R2', which is derived from record constructor of 'R'. */
end 'M'

In Flat Modelica the scope of the short class declaration (see 'R2') is restricted such that one can only find constants.

Use cases of having declaration equations in records:

Constant value modifications of a type allowed?

record 'R'
  Real 'x' = 1.0;
  constant Real 'c' = 1.0;
  parameter Real 'p' = 1.0;
  parameter Real 'p2' = 2 * 'c';   /* Will modify the default of 'p2' when 'c' is modified, adds significant complexity. */
end 'R';

model 'M'
  'R' 'r_const'('x' = 2, 'c' = 3, 'p' = 4);

  'R' 'r_var'(
    'x' = 2 * 'r_var'.'c' * time,        /* Could be turned into an equation. */
    'c' = 3,
    'p' = 4);

  'R' 'r_eq'('c' = 3, 'p' = 4);
equation
 'r_eq'.'x' = 2 * 'r_eq'.'c' * time;  /* Requires the modifier of 'r_eq'.'x' being removed. */
 'r_eq'.'p2' = 2 * 'r_eq'.'c';        /* Does not appreciate that 'p2' is not final, strictly speaking this can no longer be modified. */
end 'M'

For constants we require to have a declaration equation.
Should this apply to constants in records also?

Record constructors rely on the declaration equations for building the constructor function.

@henrikt-ma henrikt-ma added the MCP0031 Base Modelica and MLS modularization (MCP-0031) label Dec 3, 2020
@henrikt-ma
Copy link
Collaborator Author

I think we should revisit the questions and examples above with inspiration from what we did regarding the so-called variability-constrained types: https://github.com/modelica/ModelicaSpecification/blob/MCP/0031/RationaleMCP/0031/variability-constrained-types.md. To me, a record with non-constant declaration equations (which is only possible by referencing other members of the same record) seems like something that one would at most need for use in model component declarations. Even there, however, I wonder why we – as a first simple approach – don't require equations to be given separately in the model rather than through the record type definitions.

@henrikt-ma
Copy link
Collaborator Author

henrikt-ma commented Feb 8, 2022

Here are some full Modelica examples relying on record members being in scope inside the record itself. They would complicate the Flat Modelica type concept considerably, so it would be great if we could convince ourselves that such access could be forbidden in Flat Modelica.

model ArrayDimWithValue
  record R
    constant Integer n = 3;
    Real[n] x;
  end R;

  R r(x(start = linspace(0.0, 1.0, r.n), each fixed = true));
equation
  der(r.x) = -r.x;
end ArrayDimWithValue;

A more advanced variant (which Modelica tools can handle this?):

model ArrayDimWithoutValue
  record R
    constant Integer n;
    Real[n] x;
  end R;

  R r(n = 3, x(start = linspace(0.0, 1.0, r.n), each fixed = true));
equation
  der(r.x) = -r.x;
end ArrayDimWithoutValue;

Use in a built-in attribute modification, with array size depending on record member:

model StartAttrSize
  record R
    constant Integer n = 3;
    Real[3] x(start = linspace(0.0, 1.0, n), each fixed = true);
  end R;
  …

Similar to the above, but with only values depending on record member:

model StartAttrValues
  record R
    constant Integer n = 3;
    Real[3] x(each start = n, each fixed = true);
  end R;
  …

Constant value modification:

model ConstantValueModification
  record R
    constant Real c = 1.5;
    Real x = c; /* Note: c is a constant expression. */
  end R;

  R r1;
  R r2(c = 2.5);
end ConstantValueModification;

Non-constant value modification:

model NonConstantValueModification
  record R
    Real x = 1.5;
    Real y = x; /* Note: x is a continuous-time expression. */
  end R;

  R r1;
  R r2(x = 2.5);
  R r3(y = 2.5);
end NonConstantValueModification;

@casella
Copy link
Collaborator

casella commented Feb 8, 2022

@henrikt-ma, for my information, are those examples meant to be (possibly) valid also in Modelica (I understand they currently aren't), or only in Flat Modelica?

@henrikt-ma
Copy link
Collaborator Author

henrikt-ma commented Feb 8, 2022

@henrikt-ma, for my information, are those examples meant to be (possibly) valid also in Modelica (I understand they currently aren't), or only in Flat Modelica?

The intention is not to make any extensions of full Modelica, only to put up a range of examples of possibly valid Modelica, so that we can discuss which of these we'd need to support in Flat Modelica. If we can rule out some of them as not valid full Modelica today, that is probably a good reason for not worry about how to support them in Flat Modelica. (Sometimes, though, simplifications in one end of Flat Modelica can require it to be slightly extended in another end to compensate. Let's hope this is not the case this time.)

@olivleno
Copy link
Collaborator

olivleno commented Feb 22, 2022

Web Meeting (Hans, Henrik, Gerd, Martin, Oliver)

Looking at example ArrayDimWithValue:

Henrik: We could make a similar assumption as for function default arguments by automatically generating variants that avoids the record internal dependency on n.

Hans: Lift all modifications to the use of the record:

model M 
  parameter Integer n=r.n; 
  R r(x(start=linspace(0,1.0,n)); 
end M;

Henrik: If look-up inside a record was disallowed in Flat Modelica and being expressed by lifting.

Hans: Record members should be disallowed to modify values of members, but may be used to size other members.

Gerd: Changing n basically creates a new type.

Henrik: In Flat Modelica this new type could be expressed explicitly. I don't expect an explosion of types as the typical usage is related to applications like the number of phases in electrics.

The example ArrayDimWithoutValue would look like this in Flat Modelica:

package ArrayDimWithoutValue
  record _R3
    constant Integer n=3;
    Real[3] x;
  end _R3;

  model ArrayDimWithoutValue
    _R3 r( x(start = linspace(0.0, 1.0, 3), each fixed = true));  // this could have been r.n instead of 3
  equation
    der(r.x) = -r.x;
  end ArrayDimWithoutValue;
end ArrayDimWithoutValue;

Gerd: The rule when to generate a new record type can be complicated.

Henrik: No, you would keep a list of types already created.

Hans: This does not compose well for records in records, e.g. using tables nested in records.
This could be avoided by flattening records.

Nested records example from library storing 3D visualization data

record Point
  Real x[3];
end Point;

record Area
  Integer n, m;
  Point points[n];
  Point edges[m,2]
end Area;

record Object
  Integer k
  Area areas[k];
end Object;

impure function paint
  input Object obj;
algorithm
  for a in obj.areas loop
    paint_area(a);
  end loop;
end paint;

impure function paint_area
  input Area area;
external "C"
  paint_area_ext(area.n, area.points, area.m, area.edges);
end paint;

Brings us back to arrays of non-homogeneous types:

Differences.md says we either have constant or flexible sizes in arrays. Applying flexible sizes to this example means in Flat Modelica:

record 'Point'
  Real 'x'[3];
end 'Point';

record 'Area'
  Integer 'n', 'm';
  Point 'points'[:];
  Point 'edges'[:,2]
end 'Area';

record 'Object'
  Integer 'k'
  Area 'areas'[:];
end 'Object';

impure function 'paint'
  input Object 'obj';
algorithm
  for 'a' in 'obj'.'areas' loop
    'paint_area'('a');
  end loop;
end 'paint';

impure function 'paint_area'
  input 'Area' 'area';
external "C"
  paint_area_ext('area'.'n', 'area'.'points', 'area'.'m', 'area'.'edges');
end 'paint';

Does this example make sense using flexible sizes?

Gerd: Replacing sizes with colons means that all size variants are of the same type.

Henrik: Early we stated that we do not allow flexible size in model component declarations.
We can have it in functions, but how about model component declarations?
Do we run into an issue as soon as the above Object and Area become part of a model?

@olivleno
Copy link
Collaborator

olivleno commented Mar 1, 2022

Web Meeting (Martin, Hans, Henrik, Gerd, Oliver)

Gerd: Another example: reading an array of unknown size from a file.

Hans: Depends on when the initialization is performed, could be before generating flat Modelica.
Distinguish:

  1. heterogeneous array; record of records of variable size
  2. array of unknown size: e.g. read from file

Consequence of allowing flexible arrays in general?

  • Will require additional design work on flat Modelica and we tried to get rid of it until today.

Consequence of not allowing flexible arrays in general?

  • Will require to hide flexible sizes through e.g. an external object reading the file.
  • Flexible arrays will have to be evaluated as constant size when ever possible.
  • Will not be able to cover everything initially.

Conclusion on are flexible size arrays

  • Currently allowed inside functions, which makes sense.
  • More than that following model component declarations of flexible size are reasonable
    • Representation of graphical compounds (see paint example above).
    • Runtime reading of interpolation data from file.
    • Remove the need to refer to other record members to declare array sizes inside records.

Poll:

  1. Try to design something to see if this is feasible with little effort.
  2. Continue with flexible arrays in functions only and accept that examples exist that won't work.

in favor of
Option1: Henrik,
Option2: Gerd, Hans, Martin
abstain: Oliver

Decision:
Continue with option 2 and accept the limitations.

@olivleno
Copy link
Collaborator

olivleno commented Mar 1, 2022

Web Meeting (Martin, Hans, Henrik, Gerd, Oliver)

Consequences of record members not being in the same scope:

  • Example ArrayDimWithoutValue:
    • Solved by replacing n with a literal in the types of other record members
  • Example StartAttrValues: Highlight the
model StartAttrValues
  record R
    constant Real x_max = 1.0;
    Real[3] x(start = linspace(0.0, x_max, 3), each fixed = true);
  end R;
  …

flat Modelica variant 1

package StartAttrValues
  record _R3
    constant Real x_max=1.0;
    Real[3] x;
  end _R3;

  model StartAttrValues
    _R3 r( x(start = linspace(0.0, r.x_max, 3), each fixed = true)); 
  equation
    der(r.x) = -r.x;
  end StartAttrValues;
end StartAttrValues;

flat Modelica variant 2

package StartAttrValues
  record _R3
    constant Real x_max=1.0;
    Real[3] x(start = linspace(0.0, 1.0, 3), each fixed = true);  //using literal in record def.
  end _R3;

  model StartAttrValues
    _R3 r; 
  equation
    der(r.x) = -r.x;
  end StartAttrValues;
end StartAttrValues;

Henrik: In flat Modelica we already know all modifications. Variant 2 looks fragile but it's as valid as variant 1.

@olivleno
Copy link
Collaborator

olivleno commented Mar 17, 2022

Web Meeting (Henrik, Hans, Gerd, Martin, Oliver)

  • Example1 StartAttrSize:
model StartAttrSize
  record R
    constant Integer n = 3;
    Real[3] x(start = linspace(0.0, 1.0, n), each fixed = true);
  end R;

  R r;

flat Modelica variant 1 (use model component declaration modification)

package StartAttrSize
  record _R3
    constant Integer n = 3;
    Real[3] x;
  end _R3;

  model StartAttrSize
    _R3 r( x(start = linspace(0.0, 1.0, r.n), each fixed = true)); 
  end StartAttrSize;
end StartAttrSize;

flat Modelica variant 2 (use record component declaration modification)

package StartAttrSize
  record _R3
    constant Integer n = 3;
    Real[3] x(start = linspace(0.0, 1.0, 3), each fixed = true);  //using literal in record def.
  end _R3;

  model StartAttrSize
    _R3 r; 
  end StartAttrSize;
end StartAttrSize;

flat Modelica variant 3: (remove record entirely)

package StartAttrSize
  model StartAttrSize
    constant Integer 'r.n' = 3;
    Real[3] 'r.x'(start = linspace(0.0, 1.0, 3), each fixed = true);  //using literal in record def.

  end StartAttrSize;
end StartAttrSize;

Conclusion:
As long as we use the record with one value for n at the time, there is nothing difficult about it.

  • Example2 StartAttrSize: (ragged model array)
model StartAttrSize
  record R
    constant Integer n = 3;
    Real[n] x(start = linspace(0.0, 1.0, n), each fixed = true);
  end R;

  R[2] r(n={4, 5});

flat Modelica variant 1

package StartAttrSize
  record _R4
    constant Integer 'n' = 4;
    Real[4] 'x';
  end _R4;

  record _R5
    constant Integer 'n' = 5;
    Real[5] 'x';
  end _R5;

  model StartAttrSize
    _R4 'r[1]'( 'x'(start = linspace(0.0, 1.0, 'r[1]'.'n'), each fixed = true)); 
    _R5 'r[2]'( 'x'(start = linspace(0.0, 1.0, 'r[2]'.'n'), each fixed = true)); 
  end StartAttrSize;
end StartAttrSize;

flat Modelica variant 2
similar as for variant 1

flat Modelica variant 3: Remove record entirely

package StartAttrSize
  model StartAttrSize
    constant Integer 'r[1].n' = 4;
    Real[4] 'r[1].x'(start = linspace(0.0, 1.0, 4), each fixed = true);  //using literal in record def.

    constant Integer 'r[2].n' = 5;
    Real[5] 'r[2].x'(start = linspace(0.0, 1.0, 5), each fixed = true);  //using literal in record def.

  end StartAttrSize;
end StartAttrSize;

Conclusions:
This works as long as only constant indexing inside the array is required.

  • Example that cannot be handled
model StartAttrSize
  record R
    constant Integer n = 3;
    Real[n] x(start = linspace(0.0, 1.0, n), each fixed = true);
  end R;

  R[2] r(n={4, 5});
  parameter Integer i=1;

  Real var1=sum(r[i].x);
  Real var2=r[i].x[1];

Could be handled by forcing i to be considered as evaluated parameter and turned into a constant.

Poll:
Option 1: This limitation for ragged arrays is acceptable for now, as it is problematic in full Modelica also.
Option 2: This needs to be elaborated in more detail to find a better solution.

in favor of option 1: Henrik, Martin, Hans, Gerd, Oliver
in favor of option 2: none

Decision:
Accept the limitation for first version of flat Modelica.

@olivleno
Copy link
Collaborator

Web Meeting: Hans, Henrik, Gerd, Martin, Oliver

Looking at ArrayDimWithValue this is a special case of the more complicated case ArrayDimWithoutValue which has been resolved.

Constant value modification:

Example from above:

model ConstantValueModification
  record R
    constant Real c = 1.5;
    Real x = c; /* Note: c is a constant expression. */
  end R;

  R r1;
  R r2(c = 2.5);
end ConstantValueModification;

Constant value modification: flat Modelica Variant 1 (use model component declaration modification)

package 'ConstantValueModification'
  record 'R'
    constant Real 'c' = 1.5;
    Real 'x';   /* Removed "= c"*/
  end 'R';

  model 'ConstantValueModification'
    'R' 'r1'('x' = 'r1'.'c');
    'R' 'r2'('c' = 2.5, 'x' = 'r2'.'c');  /* added modification here*/
  end 'ConstantValueModification';

end 'ConstantValueModification';

Constant value modification: flat Modelica Variant 2 (use record component declaration modification)

package 'ConstantValueModification'
  record _R1
    constant Real 'c' = 1.5;
    Real 'x' = 1.5;   /* Replaced "c" with literal*/
  end _R1;

  record _R2
    constant Real 'c' = 2.5;
    Real 'x' = 2.5;   /* Replaced "c" with literal*/
  end _R2;

  model 'ConstantValueModification'
    _R1 'r1';  /*removed modifications*/
    _R2 'r2';  /*removed modifications*/
  end 'ConstantValueModification';

end 'ConstantValueModification';

Constant value modification: flat Modelica Variant 3 (remove record entirely)

package 'ConstantValueModification'

  model 'ConstantValueModification'
    constant Real 'r1.c' = 1.5;
    Real 'r1.x' = 'r1.c';

    constant Real 'r2.c' = 2.5;
    Real 'r2.x' = 'r2.c';
  end 'ConstantValueModification';

end 'ConstantValueModification';

Non-constant value modification:

model NonConstantValueModification
  record R
    Real x = 1.5;
    Real y = x; /* Note: x is a continuous-time expression. */
  end R;

  R r1;
  R r2(x = 2.5);
  R r3(y = 2.5);
end NonConstantValueModification;

Non-constant value modification: flat Modelica Variant 1 (use model component declaration modification)

package 'NonConstantValueModification'
  record 'R'
    Real 'x' = 1.5;
    Real 'y';   /* Removed "= x"*/
  end 'R';

  model 'NonConstantValueModification'
    'R' 'r1'('y' = 'r1'.'x');   /*added modification using x*/
    'R' 'r2'('x' = 2.5, 'y' = 'r2'.'x');   /*added modification using x*/
    'R' 'r3'('y' = 2.5);
  end 'NonConstantValueModification';

end 'NonConstantValueModification';

NonConstant value modification: flat Modelica Variant 2 (use record component declaration modification)

Doesn't work because non-constant cannot be evaluated to a literal.

Constant value modification: flat Modelica Variant 3 (remove record entirely)

package 'NonConstantValueModification'
  model 'NonConstantValueModification'
    Real 'r1.x' = 1.5;
    Real 'r1.y' = 'r1.x';

    Real 'r2.x' = 2.5;
    Real 'r2.y' = 'r2.x';

    Real 'r3.x' = 1.5;
    Real 'r3.y' = 2.5;

  end 'NonConstantValueModification';
end 'NonConstantValueModification';

Conclusions for constant and non-constant value modifications

Option1: Forbid access to members of a record from inside the same record class definition

  • All above variants are allowed. It's the tools choice to use the one or the other.
  • All considered cases have been successfully resolved without issues, assuming restrictions for heterogeneous arrays apply as decided.
  • The only accessible remaining are constants and literals. This makes handling very easy as it can be assumed that flat Modelica types contain only constant modifications of values and attributes.
  • There are no obvious other cases not yet considered.

Option2: Keep looking for reasons why access cannot be forbidden

  • Explore further what could go wrong.

Hans: Happy for what we see now at least.
Gerd: Possible problem if there is a reference to another component and this value is modified later, but this should be recognized by this approach.

Poll:
in favor of Option1: Henrik, Gerd, Hans
in favor of Option2: none
abstain: Martin, Oliver

Decision:
Access to record members will be forbidden

Next:
Revisit to discuss whether we want to generalize this decision to any record class definition.

@henrikt-ma
Copy link
Collaborator Author

henrikt-ma commented Mar 23, 2022

The example to discuss next meeting is whether we want to support this kind of access to constant members of a record:

package 'M'
  record 'R'
    constant Real 'c' = 1.5;
  end 'R';
  model 'M'
    Real 'x' = 'R'.'c'; /* Is accessing a constant member in a record definition allowed? */
  end 'M';
end 'M';

For comparison, it is already clear that this should be allowed in Flat Modelica:

package 'M'
  constant Real 'c' = 1.5;
  model 'M'
    Real 'x' = 'c'; /* Clearly allowed package constant access. */
  end 'M';
end 'M';

Also note the following clearly allowed variant of the above:

package 'M'
  record 'R'
    Real 'c' = 1.5;
  end 'R';
  constant 'R' 'r';
  model 'M'
    Real 'x' = 'r'.'c'; /* Clearly allowed package constant access. */
  end 'M';
end 'M';

@olivleno
Copy link
Collaborator

Web Meeting (Hans, Henrik, Martin, Oliver)

Discussion of the examples above:
The first example accessing a class member of a record works in full Modelica but should be disallowed in flat Modelica, because:

  1. for simplicity of the language
  2. No benefit from keeping the structure in flat Modelica

Is there a need to treat records like package constants?
No.

Conclusion:
In flat Modelica we will never treat a record as if it was a package.
Hence, generalize the above from "in the same record class definition" to "any record definition".

@@ -1,6 +1,44 @@
# Semantical differences between Flat Modelica and Modelica
This document describes differences between Flat Modelica and Modelica that aren't clear from the differences in the grammars.

## Lexical scoping inside record definitions
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should contain two subsection covering
inside records
not treating records as packages

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

@henrikt-ma henrikt-ma marked this pull request as ready for review March 29, 2022 14:23
Co-authored-by: Hans Olsson <HansOlsson@users.noreply.github.com>
@olivleno olivleno merged commit e1f6473 into MCP/0031 Apr 5, 2022
@olivleno olivleno deleted the MCP/0031+value-modification branch April 5, 2022 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
MCP0031 Base Modelica and MLS modularization (MCP-0031)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants