Skip to content

Commit

Permalink
Merge branch 'MCP/0031' into MCP/0031+name-of-the-game
Browse files Browse the repository at this point in the history
  • Loading branch information
henrikt-ma committed May 4, 2023
2 parents 7e291bb + 820dad4 commit 9fb1c6e
Show file tree
Hide file tree
Showing 6 changed files with 394 additions and 23 deletions.
15 changes: 8 additions & 7 deletions RationaleMCP/0031/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ Use cases to have in mind in the design of Base Modelica, also indicating the us
* Serve as intermediate stage in the Modelica specification, separating front end matters (the high level constructs of the Modelica language) from back end matters (simulation semantics).
- Generally speaking, the two different matters will attract attention from people with quite different interests and areas of expertise (compuer science and numerical mathematics, respectively).
- Separation will facilitate more efficient work and rapid development of the two aspects of the Modelica language.
- Simulation semantics could then get some well deserved attention after many years of almost no attention at all, which would be a necessary step towards true portability of models and libraries between tools.
- Simulation semantics could then get some well deserved attention, which would be a necessary step towards true portability of models and libraries between tools.
- Making it easier to organize the development work of a Modelica tool.
- A working gorup with focus on the equation model and simulation semantics would also play a very important roll in future developments of new language features such as varying-structure systems, or integration with PDE solvers.
- A working group with focus on the equation model and simulation semantics would also play a very important roll in future developments of new language features such as varying-structure systems, or integration with PDE solvers.
* Basis for the _Equation Code_ of eFMI, [see below](#Relation-to-eFMI).
* Help users understand the mysterious ways of the Modelica language by showing them the lowered models.
* Comparison of different Modelica back ends with the same lowered model.
Expand Down Expand Up @@ -71,11 +71,12 @@ These are subtopics that are considered necessary to resolve for a first version
- [x] Get rid of `each`. [PR](https://github.com/modelica/ModelicaSpecification/pull/2583)
- [x] Investigate need for `for`-equations. [Design](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031-for-equations/RationaleMCP/0031/grammar.md#b26-equations), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/3163)
- [x] Marking of top level inputs and outputs. [Design](differences.md#Input-output)
- [ ] Add function `realParameterEqual` for use in automatically generated asserts on `Real` equality. [Design](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Breal-equality/RationaleMCP/0031/differences.md#connect-equations), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/3175)
- [ ] Figure out what to do with synchronous features.
- [ ] Event handling semantics is preserved as in Modelica.
- [ ] Source locations pointing back to the original Modelica code.
- [ ] Settle the name (currently _Base Modelica_), considering that scalarization isn't mandatory.
- [x] Add function `realParameterEqual` for use in automatically generated asserts on `Real` equality. [Design](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Breal-equality/RationaleMCP/0031/differences.md#connect-equations), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/3175)
- [x] Figure out what to do with synchronous features. [Design](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Bsynchronous/RationaleMCP/0031/differences.md#clock-partitions), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/3240)
- [x] Event handling semantics is preserved as in Modelica.
- [x] Get rid of `when initial()` and `when`-equations inside `if` and `for`. [Design](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Bsimplify-when/RationaleMCP/0031/differences.md#when-equations), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/3258)
- [x] Source locations pointing back to the original Modelica code. [Design](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Bsource-locations/RationaleMCP/0031/source-locations.md), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/3295)
- [ ] Settle the name (originally _Flat Modelica_), considering that scalarization isn't mandatory. [Design](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Bname-of-the-game/RationaleMCP/0031/name-of-the-game.md), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/3224)

### Base Modelica 0.1+…1.0 (future MCPs)
In future minor versions of Base Modelica 1, we could improve the language by incorporating smaller improvements that were not considered necessary for version 1.0.
Expand Down
12 changes: 12 additions & 0 deletions RationaleMCP/0031/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ These are all the non-vendor specific annotations inherited from full Modelica t

These are the new annotations introduced in Base Modelica, each explained in more detail below:
- [`Protected`](#protected) — Indicate whether component declaration comes from protected section in original full Modelica model
- [`solverMethod`](#solvermethod) — Discretization method for a clocked sub-partition


### `Protected`
Expand Down Expand Up @@ -103,6 +104,17 @@ package 'M'
end 'M';
```

### `solverMethod`

Form:
```
String solverMethod = "ImplicitEuler";
```

The `solverMethod` annotation is only allowed on a `subpartition`.
The recognized method names are the same as for the `solverMethod` named argument of the `Clock` constructor in full Modelica.



## Vendor annotations

Expand Down
211 changes: 209 additions & 2 deletions RationaleMCP/0031/differences.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ and some tools attempt to evaluate the parameters even if the branches have the

Base Modelica is designed to avoid such implicit evaluation of parameters, and thus this restriction is necessary.

In Modelica a separate issue is that `if`-equations may contain connect and similar primitives
that cannot easily be counted; but they are gone in Base Modelica.
In Modelica a separate issue is that `if`-equations may contain `connect` and similar primitives that cannot easily be counted; but they are gone in Base Modelica.


## Conditional components
Expand All @@ -97,6 +96,130 @@ All checks that apply to inactivated components in Full Modelica will need to be
The full Modelica PR https://github.com/modelica/ModelicaSpecification/pull/3129 regarding conditional connectors is expected to make this restriction easier to handle when generating Base Modelica.


## Connect equations

There are no `connect` equations in Flat Modelica.

To make this possible, a new builtin function `realParameterEqual` is provided.
The two arguments to `realParameterEqual` must be `Real` parameter expressions, and the result is a `Boolean` (of variability determined from the arguments as usual).

The function returns `true` if and only if the two arguments are exactly equal up to the precision of a stored parameter value.
The non-trivial case for the function is thus when one or both of the arguments have extra bits of precision stored in registers, as illustrated by the example `ExtraPrecisionProblems` below.

For a basic example of how the function can be used, consider the following model:
```
model M
connector C
parameter Real p;
end C;
model A
C c;
end A;
A a1(c.p = 1.0);
A a2(c.p = 1.1);
equation
connect(a1.c, a2.c);
end M;
```
In Flat Modelica one needs to use the `realParameterEqual` function:
```
package 'M'
model 'M'
parameter Real 'a1.c.p' = 1.0;
parameter Real 'a2.c.p' = 1.0;
equation
assert(realParameterEqual('a1.c.p', 'a2.c.p'), "Connector parameters a1.c.p and a2.c.p must be equal due to connect equation.");
end 'M';
end 'M';
```

Regarding the problem with extra bits of precision hiding in registers, consider the following model:
```
model 'M'
parameter Real 'p' = 1.1;
parameter Real 'q' = sin('p');
equation
/* While 'q' has the precision of a Real stored in memory, the value of sin('p') might exist with
* higher precision in a register. When comparing the two, realParameterEqual must make sure that
* the extra bits of precision does not make the assertion fail.
*/
assert(realParameterEqual('q', sin('p')), "Incorrect implementation of realParameterEqual!");
end 'M';
```


## When-Equations

The `when`-equations in Flat Modelica are more restricted compared to full Modelica.
In summary:
- `when`-equations have no meaning at all for the initialization problem:
* No special treatment of `initial()` as a `when`-clause trigger expression.
* No implicit initial equations in the form `x = pre(x)` for a variable `x` assigned in the `when`-equation.
- It is not allowed to have `when`-equations inside `if`-equations and `for`-equations.

Here, the _special treatment_ of `when initial() then` refers to the special meaning of such a `when`-equation in the initialization problem, including the special meaning of `reinit` when activated by `initial()`.
Hence, the first `when`-clause triggered by `initial()` in full Modelica needs to be turned into `initial equation` form in Flat Modelica, with `reinit`-equations replaced by equality-equations.
This also means that in Flat Modelica, the triggering condition `initial()` will have the same effect as the triggering condition `true and initial()`, namely that they will never trigger the `when`-clause because the expression never undergoes a positive edge.

The implicit initial equations `x = pre(x)` in full Modelica (in case no `when`-clause is activated with `initial()`) need to be made explicit in Flat Modelica.

Regarding `when`-equations inside `if`-equations and `for`-equations, full Modelica only allows this where the `if`-equation conditions and `for`-equation ranges are parameter expressions.
Hence, it is only with a small loss of generality that it is being assumed that these conditions and ranges should be possible to evaluate during translation, allowing an `if`-equation to be reduced to one of its branches, or a `for`-equation to be unrolled.


## When-Statements

Unlike the `when`-equations, there are no restrictions on the `when`-statements in Flat Modelica relative to full Modelica.

Note that `reinit` is not allowed in a `when`-statement, so the notable thing about `when`-statements in Flat Modelica is that they may be triggerd by `initial()` just like in Full Modelica.

### Rationale

The reason for not further restricting the `when`-statements is that it it was considered too complicated to reduce `when initial()` in an algorithm to something more elementary. As an example, consider the following full Modelica `when`-statement with a clause triggered by `initial()`:
```
Real x;
Real y;
algorithm
x := 0.5;
x := x + time;
when {x^2 > 2.0, initial()} then
y := pre(x);
end when;
x := x + 0.5;
```

Note that putting the following `if`-statement in the algorithm would be illegal since the argument of `pre` needs to be discrete-time:
```
if initial() then
'y' := pre('x'); /* 'x' is continuous-time, since no longer inside when-clause. */
end if;
```

To get around this problem, one could try making separate variants of the algorithm depending on `initial()`:
```
Real 'x';
Real 'y';
initial algorithm
'x' := 0.5;
'x' := 'x' + time;
'y' := pre('x'); /* 'x' is discrete-time, since inside initial algorithm. */
'x' := 'x' + 0.5;
algorithm
if not initial() then
'x' := 0.5;
'x' := 'x' + time;
when 'x'^2 > 2.0 then
'y' := pre('x');
end when;
'x' := 'x' + 0.5;
end if;
```

However, this doesn't work either, as the initialization problem will have two algorithms assigning to `'x'` and `'y'`, even though one of the algorithm has a disabled body.


## Pure Modelica functions

In addition to full Modelica's classification into _pure_ and _impure_, Base Modelica adds the concept of a `pure constant` function, informally characterized by the following properties:
Expand Down Expand Up @@ -1180,3 +1303,87 @@ Accordingly, a function component declaration which is neither input nor output

The new annotation `protected = true` provides a standardized way to indicate that a component declaration in Base Modelica comes from a protected section in the full Modelica model.
See [`protected` annotation](annotations.md#protected).


## Clock partitions

The implicit clock partitioning carried out by tools for full Modelica is made explicit in Flat Modelica.
The equations solved in a clocked sub-partition are placed in a dedicated `subpartition` construct, and the variables being determined by the sub-partition can be determined by a simple inspection of the equations, as explained below.
See _base-partition_ and related rules in the [grammar](grammar.md#clock-partitions) for details on the syntax.

Note that the component declarations for variables solved in a sub-partition are not syntactically placed inside the `subpartition` construct because of the way that the sub-clocks and base-clocks cut across the instance hierarchy.

In Flat Modelica, every clock is declared as a component with a name, at the top of some `partition`.
Sometimes, this name will correspond to the name of a clock in full Modelica, sometimes it will be an automatically generated name.
Tools may find it useful to include the clock components in a simulation result, but doing so is not required and there is no standard for what to store.

Instead of the binary clocked `sample` operator in full Modelica, Flat Modelica has an unary `sample(…)` operator.
It is only allowed inside the equations and algorithms of a `subpartition`, and the semantics is that the argument expression is sampled at the clock ticks of the current sub-partition.

A Flat Modelica model with clock partitioning can look like this:
```
package 'M'
model 'M'
Real 'x';
Real 'baseVar', 'cVar1', 'cVar2', 'cVar3';
Real 'mixedVar1';
/* Equations and algorithms before the start of the first base-partition belong to the continuous-time partition. */
equation
der('x') = 1;
partition /* Beginning of base-partition */
Clock 'myClock' = Clock(1); /* Clock name originating from full Modelica model. */
Clock _subClock0 = subSample('myClock', 2); /* Automatically generated clock name. */
Clock _subClock1 = superSample(subSample('myClock', 2), 8);
subpartition (clock = 'myClock') /* Beginning of sub-partition within base-partition. */
equation
'baseVar' = sample('x');
subpartition (clock = _subClock0, solverMethod = "ImplicitEuler")
equation
der('cVar1') = noClock('baseVar');
subpartition (clock = _subClock1)
equation
'cVar2' = noClock('baseVar');
'cVar3' = noClock('cVar1');
algorithm
'mixedVar1' := 'cVar2' + 'cVar3';
partition
Clock _baseClock0 = Clock(1.1);
...
/* Base-partition ends at the start of another base-partition, or at the end of the model. */
end 'M';
end 'M';
```

A `partition` begins with the definition of all clocks belonging to the base-partition and its sub-partitions.
A clock is only accessible within the `partition` where it is declared, and may only be used to define other clocks and to specify the `clock` of a `subpartition`.

A sub-partition begins with the `subpartition` keyword, followed by a specification of some sub-partition details in the form of the named argument syntax.
The following are the only valid named arguments:
- `clock` `=` _clock-name_
- `solverMethod` `=` _method-name_

Here, _clock-name_ must be the name of a `Clock` declared within the current `partition`, and _method-name_ must be a constant `String` expression.
`clock` is required for every `subpartition`.
`solverMethod` is only required when the sub-partition contains continuous-time equations, and specifies the time discretization method.
It is an error if a named argument is specified multiple times.

In the equations and algorithms of a `subpartition`, references to variables from the continuous-time partition must appear inside the Flat Modelica unary `sample(…)` operator.
Similarly, references to variables from another sub-partition must appear inside the `noClock(…)` or `previous(…)` operators.
It is not allowed to reference variables determined in another clocked base-partition, except when wrapped in `hold()`.
(The expression `hold(x)` is a continuous-time expression and needs to be sampled before it can appear in a clocked partition.)
Hence, the variables determined by a `subpartition` are found as all component references appearing in the `subpartition`'s equations and algorithms, except:
- Parameters and constants.
- Variables inside `noClock(…)`, `sample(…)`, or `previous(…)`.

The `noClock(…)` may only be used to refer to variables determined by an earlier `subpartition` of the same `partition`.
This means that there cannot be cyclic dependencies between the sub-partitions, and that evaluation of a `partition` at a clock tick can always be performed by executing the `subpartitions` in order of appearance.
Note that `noClock(…)` in may sometimes be wrapped around a variable in Flat Modelica where there was no wrapping in the original full Modelica model.

Note that if we want to extend Flat Modelica to be used as sub-components this implies that we have to decide whether to clock the component or not; that is similar to the need for external sampling in eFMI.
2 changes: 2 additions & 0 deletions RationaleMCP/0031/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,5 @@ Maybe also fill, etc affected?
* ~~getInstanceName~~ (handled before Base Modelica; there is no instance name anymore)

## New in Base Modelica

* [realParameterEqual](differences.md#connect-equations) (test exact equality of Real parameters)
Loading

0 comments on commit 9fb1c6e

Please sign in to comment.