diff --git a/RationaleMCP/0031/99thDesignMeetingNotes.md b/RationaleMCP/0031/99thDesignMeetingNotes.md new file mode 100644 index 000000000..5a7ddf559 --- /dev/null +++ b/RationaleMCP/0031/99thDesignMeetingNotes.md @@ -0,0 +1,41 @@ +# From the 99th Modelica Design Meeting minutes + +Agreement to proceed, but details unclear and need rapid progression. +Tools should be able to handle both – either proper subset or major overlap. + +A proper subset might possible – in particular the drawbacks of quoting seem acceptable. + +Add the following to Modelica (seems possible in Dymola, OpenModelica, MapleSim, Wolfram SystemModeler) +``` +foo()[i] (…)[i] +foo().r (…).r +``` + +Separate tasks: +* Name mapping (quoting etc) + - Separate name-space for automatically generated variables (perhaps not quoted). +* Subset of grammar + - No extends. + - No package structure – or have user-defined functions with “.” in their name. + - Have user-defined function at start; but tight time-constraint – maybe in 2nd implementation phase in Emphysis +* Annotating the source with source-location (hopefully a general solution for Modelica; also for Buildings-models generated by Python-script, CATIA kinematics, …). Not standardize too much since depends on tool-chain, but might suffice with container – saying that it is a source location, but leave details to the tool. The location should be usable by the producer of the flat Modelica (URI – or file name+line+column). LLVM have some smart variant. Unclear if we want locations on sub-expressions, and/or multiple source locations for one equation (e.g. for flow-equation based on connect-statements). Many structural parameters must be constant-folded away (e.g. conditions for unbalanced if-then-else). +* How similar is FlatModelica to Modelica: + - Shall parameter/constant be removed in this format – since they are evaluated in other places, or kept since part of the result? + - Can a FlatModelica model be used as base-class? Should it behave the same in that case? Easiest to first forbid. + - Alias-elimination etc: Allowed, but not required. + - Are we losing some information, e.g. start-value priority + - Have “Flat Modelica” as a separate “class”? +Reorganization of Modelica Specification – combined or separate step? + +How to separate FlatModelica and Modelica: +* One possibility is a special annotation; and in that case un-mangle names etc. +* Another possibility is to say “flatmodel” instead of “model” – and thus have a different grammar. + +Prioritize these steps with responsible: +1. Subset of grammar [M. Sjölund; working in Emphysis] (We already have algorithm part in Emphysis – but that is a different sub-set); might be best to have separate grammar in case it is not a full subset. Modelica grammar (current one is about LL(k)) with some removed productions – prefer to have in similar format. Also good to have proper Modelica-parser – based on exact grammar in specification. +2. Name mapping [H. Tidefelt]. Use quoted identifiers with standardized mangling scheme. +3. Subset of annotations [H. Olsson]. List sub-set of Modelica-annotations with specified semantics – especially the ones relevant for simulation. Others may be kept by tools. (Inline annotations for functions, vendor annotations? choices for values of components and Dialog? Experiment, HideResult, description-strings) – add source-location. +4. How to keep additional information, like start-value priority – that otherwise might be lost in FlatModelica format. What to preserve of Modelica_Services? How to handle ModelicaStandardTables reading from files when you don’t have files? +How to work on these in a transparent way? +* Different markdown files in MCP? Separate discussion from design in separate files (only summary)? Use labels for them? Have draft pull-request for branch with discussion? Have an official fork for each MCP? Comments on files + - Lennart to summarize what is possible for Github projects diff --git a/RationaleMCP/0031/Flat-Modelica-requirements.md b/RationaleMCP/0031/Flat-Modelica-requirements.md new file mode 100644 index 000000000..12da58342 --- /dev/null +++ b/RationaleMCP/0031/Flat-Modelica-requirements.md @@ -0,0 +1,141 @@ +# Requirements on Flat Modelica +This document should contain a first draft of the requirements on what Flat Modelica should and shouldn't be, to be used as a starting point for discussions at the upcoming design meeting in Regensburg on March 7-8, 2019. It also contains a rough separation of things belonging to the three main parts of the Modelica Language Specification (MLS) that would come out of this MCP. + +## Flat Modelica features + +Things that the Flat Modelica format should support: +- Basic scalar types with the same attributes as in Modelica. +- Record and enumeration types. +- Arrays. +- Component declarations (with both public and protected visibility). + - Comment (@mtiller): It doesn't seem very flat if we preserve (potentially deeply nested) components instances + - Comment (@harmanpa): This should be changed to Component declarations of the scalar types, record and enumeration types, and arrays of each; not hierarchical. Component declarations have dot-notation names. + - Remove (that is, expand) records? +- Equations and algorithms, both scalar, array-valued, and record-valued. +- Optional source locations for use in error messages. + - Add this to the formal grammar? If so, this will require some thought in order to be flexible and precise enough without interfering with too much with the rest of the grammar. + - Comment (@mtiller): Add this as standard annotations? Already supported by current grammar. + - Comment (@harmanpa): Agreed as that also allows other meta-data. Unfortunately having `annotation(__something_LineNumber=12)` on every statement could become quite verbose. +- Documentation strings. +- Vendor-specific annotations. + - Comment (@mtiller): So annotations are present in the grammar but all annotations except vendor annotations are stripped. If the concern is bloat from annotations that are no longer relevant in the flattened form (*e.g.,* graphical annotations), why not identify what gets excluded vs. what gets included? (see comment about source locations above) + - Comment (@harmanpa): I agree, I'd like to be able to put arbitary meta-data in. +- All variabilities, but constant evaluation of parameters is not allowed. (For example, this guarantees that all parameters will remain parameters if a Flat Modelica model is exported to an FMU for Model Exchange.) + - Comment (@mtiller): The semantics of this are unclear. When you say constant evaluation, do you mean constant folding? What about `final` parameters? Should those be parameters in an FMU? I wouldn't think so. It seems like they should just be `output`s. +- List of all `parameter` variables that were treated as `constant` due to use in _parameter expressions_, the `Evaluate=true` annotation, or subject to constant evaluation during flattening for other reasons. + - `final` can probably be replaced by using normal equation instead of binding equation. meaning that binding eqations can always be treated as non-final. + - Comment (@mtiller): Why not just add an `Evaluate=true` annotation to indicate these. +- Values for all constants, even those that have been inlined everywhere, since the values should be part of the simulation result. +- Less restricted forms of record field access and array subscripting? + - Comment (@mtiller): Wouldn't we expect to restrict forms of record field access and array subscripting? If so, then I would suggest saying "Restrict some forms of record field access and array subscripting" or "A more restrictive subset of record field access and array subscripting". + - Comment (@mtiller): Factor out constant subexpressions to unique variables? Perhaps filter all literals (scalars and arrays) completely out of the flattened form and provide them in a separate file? + - Needs Standardized naming for introduced intermediate variables. +- Expressions for all variables that were treated as aliases during flattening, specifying the variable that it is an alias of and the sign of the relationship + - Comment (@mtiller): Doesn't the expression already tell us all that (*e.g.,* `b = -a`...`-a` is an expression and it tells us that `b` is an alias of `a` with the opposite sign? Or did you want something more explicit? Should there be a special form of equation (perhaps in the form of an assignment statement) that can be used to indicate solved equations in general, and alias relations in particular? + - Comment (@harmanpa): Yes syntactically it is just that expression, however that isn't necessarily the expression that the alias came from. e.g. the model might contain `a = c` and `a = -b` but we decide to keep `b`, so in our aliases section we store `b = -a` and `b = -c`. +- Function declarations that are utilised in the model. + - Comment (@mtiller): Do we flatten the functions? I say that because functions can use features like `extends` or `redeclare` in their definitions. Presumably we want all that removed in a flattened form, no? Functions should probably be allowed to have arguments of array and record type. + - Comment (@harmanpa): Yes. Additionally we might have multiple versions of functions, because we might call a function with different dimension inputs. We need a naming convention for this. + +Examples of things that should be gone after flattening and shouldn't exist in Flat Modelica: +- Complex classes that may contain equations. +- Connectors. +- Conditional components. +- Un-balanced `if`-equations. +- Non-literal array dimensions (except in functions). +- Packages. +- Connect equations. +- Graphical and documentation annotations. +- All redeclarations +- Anything overloaded +- Imports +- Extends clauses? +- Hierarchical modifications? +- Package structure? + - If you keep components, restrict class definitions to a flat global namespace to avoid having to apply lookup rules. + +## New organization of MLS +This MCP proposes separation of the MLS into the three parts outlined below. + +### Shared features: Expressions and functions +Content of that should go into this section: +- Built-in functions. +- Expressions. +- External functions. +- User-defined functions. +- Restricted forms of record field access and array subscripting (some or all of the restrictions may be lifted for Flat Modelica). +- Unit expressions. +- Syntax for synchronous language elements and state machines. + +### Flat Modelica +Content of that should go into this section: +- Overview of execution model for hybrid differential-algebraic equations. +- Simulation initialization and priority of non-fixed start attributes. +- Event generation, event iteration, and other discrete-time behavior. +- Semantics of synchronous language elements and state machines. +- Solving of mixed systems. +- Hints for inlining, state selection, etc. + +### Modelica +Content of that should go into this section: +- All sorts of restricted classes and rules for how they may be used. +- Inheritance, modification, and redeclaration. +- Connectors, including stream connectors. +- Connect equations. +- Overloaded operators. +- Global and local balancing of equations and variables. +- Instantiation. +- Flattening. + +## An alternative view of grouping Modelica semantics (@mtiller) + +One advantage to this approach would be to organize the semantics of Modelica by when they apply. In this sense, I'd like to see a description of: + +- Semantics needed in order to create a Modelica editor + - Package structure + - Name lookup + - Graphical appearance + - Connection semantics + - Inheritance +- Semantics needed to represent mathematical structure and behavior + - Variables + - Equations + - Functions + +From this perspective, expressions can almost be treated as "pass through". The editor doesn't really need to +interpret them in any way. For the most part they just pass through to the flattened form (but with some +potential simplifications or restrictions, as described above). + +## Comments from regensburg design meeting (added by @HansOlsson) + +Need to support start-value priority (source location or special built-in operator, +either complete source location or just needed hiearachy level). +Do after Linkping meeting? +Need to design grammar: Sub-set of current Modelica or some additions? + +One major issue is variable declaration as we have e.g. `a[1].b` as a variable, +and also need record variables. +One possibility would be quoted identifiers - but can clash. + Can we always quote a quoted one without ambiguity? Just use anything - even base64. +Another possibility is extended grammar - but looks ambiguous. +E.g. `a[1].b` could be either a variable, or access to member of array of records. + +For declarations we need to know if array dimensions are part of variable or not. + +Might need accessing record elements using `.` and `[` for non record variables, + e.g., `f(x)[1]` and `f(x).x[1]` + +Array of components. Repeated equations (using quoted identifiers to be clear here): + `'a[1].x'='a[1].y';` + `'a[2].x'='a[2].y';` +But should be possible to preserve as array equations in some way (see new frontend for OM)? + +Need to test-implement? Current Flat-Modelica in e.g. Dymola is different: +e.g., records are not done like this (record values are missing), functions are missing. + +Pull-requests: +- Variable naming +- Source location? +- Handling modifier level for start-attribute, e.g.: Real p(start=modifierLevel(3, 5)); or extension + +Some minor comments. diff --git a/RationaleMCP/0031/MoM/2019-10-01_FlatModelica_100thDesignMeetingNotes.md b/RationaleMCP/0031/MoM/2019-10-01_FlatModelica_100thDesignMeetingNotes.md new file mode 100644 index 000000000..f05fa07b8 --- /dev/null +++ b/RationaleMCP/0031/MoM/2019-10-01_FlatModelica_100thDesignMeetingNotes.md @@ -0,0 +1,112 @@ +# Session on Flat Modelica at the 100th Modelica Design Meeting + +## Agenda +* Introduction of eFMI Equation Code +* Align goals of eFMI with Flat Modelica MCP of MAP_Lang +* Discuss the prepared simple example: PID controller +* Next steps + + +## Introduction of eFMI EqCode + +See ppt slides (https://emphysis.org/svn/EMPHYSIS/trunk/30_Work_Documents/WP3_eFMI-Standardization/WP3_1_Working_group_Eq-Code/eFMI_EqCode-Summary.pptx). + +## Align goals of eFMI with Flat Modelica MCP of MAP_Lang +Comments: +Francesco: functions are crucial +Martin: Could be realized as call to C-function or inline. +Gerd: Flattened Modelica might need few extension, e.g. annotations for index reduction and tearing. + +* EqCode is not the same as Flat Modelica, but we're heading in the same direction. +* The scope of EqCode is more narrow, never-the-less it makes sense to work on this in a joint effort considering EqCode as a MVP (minimal viable product) towards Flat Modelica. + +## Discuss the prepared simple EqCode example: PID controller +Below only the first lines of code discussed during the meeting are listed. The file will be made available once we have decided how to manage this work on github. + +``` +flat_model PID + + type _Time Real (final quantity="Time", final unit="s"); + + parameter _Time 'period' = 0.1 "Period of clock (defined as Real number)"; + constant Boolean 'enbDiscretize' = true "discretize system?"; + input Real 'r' "Connector of Real input signal"; + input Real 'y' "Connector of Real input signal"; + parameter _Time 'periodicClock.period' = period "Period of clock (defined as Real number)"; +``` + +### flat_model +* agreed to have a new kind of class + +### type +enumerations require type declarations, but types could be limited to enumerations. +Having types requires handling of modifiers, but could be avoided by allowing modifications only for build-in attributes. + +* Type declarations should be supported to avoid code repetition and improve readability. +* Support predefined scalar types: Real, Integer, Boolean. + +### type names +* Should have leading underscore ("_ ") to avoid confusion with variable names and keywords, see name-mapping and related discussion pull 2389. + +### parameter +How to treat a parameter without a value? +In this case the start value will be used, which is zero for Real. +Tools could restrict parameters having to have a value. +What is the meaning of a start value for parameters in the context of eFMI? + +Side note (added after the meeting): +On ECU there are different kinds of initializations: +- After power on the device is launched (start_up) all memory is brought into a state that is always the same no matter which state other devices are in. This means all inputs must be ignored. +- After the launch the device can be initialized. +- Anytime during operation the device can be re-initialized, e.g. after a certain event. + +If the semantics of Moldelica cannot be mapped in reasonable way parameters could be handled by applying a restriction that requires parameters to have values for EqCode to avoid deviation of Flat Modelica from Modelica. +* topic should be revisited. + +### variable names +* Always use quoted names according to name-mapping +Example has been adopted accordingly. + +## Requirements derived from the discussion +* Shall be a self-contained definition in a single file. +* Shall not be limited to scalars to enable efficient handling of multibody and similar. +* Flat Modelica shall be fully compatible with Modelica, no exceptions. +* EqCode can apply additional restrictions to Flat Modelica. + +## Issues from github and emails + +### [Michael Tiller] from github +Although I must say I disagree with the premise that Flat Modelica should be a subset of Modelica. It certainly doesn't need to be. You could reuse a huge amount of the grammar between them without having to prescribe this requirement. +* Having Flat Modelica as a separate language will sooner or later lead to compatibility problems. Therefore we see good reasons to consider Flat Modelica as a subset of Modelica. + + +### [Hans] from github +I would say that there are two alternatives - either we standardize such a mangling syntax for names, or we allow the declaration of variables with hierarchical names in Flat Modelica. +* see above variable names + +### [Henrik] from email +1) Flat Modelica needs to fulfill the needs to process full Modelica. The Equation Code Language should then impose restrictions on what Flat Modelica it allows. As an obvious example of this, Flat Modelica needs to allow functions with algorithmic bodies and side effects. Other things that come to mind here include algorithms, looping constructs, if-equations, when-equations, when-statements, etc.  +* Agreed, see above Requirements. + +2) There are many Modelica constructs that are not deeply involved in the complicated flattening process, and that need to be allowed also in Flat Modelica in order to preserve structure that is necessary for efficient handling of the equations. These include record and array types.  +* Agreed, see above Requirements. + +3) The "Identifier" section should probably be merged into the thing I did here: +https://github.com/modelica/ModelicaSpecification/pull/2389 +I think the main topic here is the relation to array and record types. Then, to avoid confusion, we need to make sure that example code isn't using any other form of identifier. (To me, the examples under "Identifier" and "Variable name" seem to contradict each other.) + +Questions +a) What is an "explicit declaration"? What would a non-explicit ("reference") look like? +An explicit variable declaration is one that has the type declaration inline, vs. using referring to type name declared elsewhere. +  +b) Isn't "statically evaluatable" just the same as a constant expression? (No need to introduce new terms.) +Good point. Will be adopted. +  +c) Why would "declare before use" be needed in a declarative setting such as Flat Modelica? +To be discussed. +  +d) Why remove "each"? This would remove structural information that would just be tedious to recover for the tool processing Flat Modelica. +To be discussed. +  +e) Why restrict "der" to only operate on a "free variable"?  +To be discussed with Kai. diff --git a/RationaleMCP/0031/MoM/2019-10-10_FlatModelica_WebMeeting.md b/RationaleMCP/0031/MoM/2019-10-10_FlatModelica_WebMeeting.md new file mode 100644 index 000000000..97584e807 --- /dev/null +++ b/RationaleMCP/0031/MoM/2019-10-10_FlatModelica_WebMeeting.md @@ -0,0 +1,43 @@ +Meeting Date: 10.10.2019 09:00 +Location: Skype Meeting + +### Participants +* Lenord Oliver (Bosch) +* Werther Kai (ETAS) +* Henrik Tildefeit (Wolfram) +* Martin Sjölund (LiU) + +# Notes + +Martin: + +Will be important to get more tool vendors behind the idea of Equation Code within EMPHYSIS. + +Henrik: + Is trying to get support also on Wolfram side. + +Oliver: + +SimulationX were quite supportive. + +# How to organize ourselves? +EqCode Language defined within the eFMI spec should be a reference to Flat Modelica. +On github a change proposal for Flat Modelica language exists: https://github.com/modelica/ModelicaSpecification/tree/MCP/0031/RationaleMCP/0031 + +Here we should share: +- Grammar for Flat Modelica +- Code examples +Proposed changes to the initial grammar can be created as pull request with the opportunity to discuss the proposals. + +Starting point: +- Use the current Modelica grammar. +- Or use the refactored and proposed Modelica grammar in antlr. +- Or use a first draft with removed parts + +Conclusion: +- Take the current grammar file an commit it to the 0031 branch and notify Adrian --> Henrik. +- Commit the examples into a subfolder: + - Original modelica model (models/*.mo) + - Generated flat modelica by tool (/*.mof) + - hand coded flat modelica (hand_coded/*.mof) + - meeting notes folder diff --git a/RationaleMCP/0031/MoM/2019-10-24_FlatModelica_Web-Meeting.md b/RationaleMCP/0031/MoM/2019-10-24_FlatModelica_Web-Meeting.md new file mode 100644 index 000000000..975dd2b72 --- /dev/null +++ b/RationaleMCP/0031/MoM/2019-10-24_FlatModelica_Web-Meeting.md @@ -0,0 +1,49 @@ +Meeting Date: 10.10.2019 09:00 Location: Skype Meeting +# Participants + + Lenord Oliver (Bosch) + Werther Kai (ETAS) + Henrik Tildefeit (Wolfram) + Gerd Kurzbach (ESI-ITI) + Michael Schellenberger (ESI-ITI) + Martin Sjölund (LiU) + +# Notes + +## Quoted names +Is fine, but quotes in quoted names are ugly. +Never-the-less needed to be compliant with Modelica as quoted names are allowed in Modelica. + +### Conclusion: +EqCode should have an additional restriction to make it more clean, but Flat Modelica requires to support it. + +## start attribute +What is the semantics of that? +An expression overrules the start value. +If there is no value or assignment, then use the start value. + +We could have flat_modelica to require not to have a start value, when an expression is present. + +Remove start entirely? +No, we need this for intialization. + +## final modifier +Should be considered to be removed from Flat Modelica. +final is relevant for the compiler, but not for the simulation. This kind of informations should be moved to annotations. +But this kind of information really affects how the equations are transformed. +The question is when are these transformations applied? +A final parameter could be considered as constant with an annotation that tell what simplifications have been applied. + +The goal is to get to a simple set of equations without loosing the information of the original model. +The semantics is applied, but the origin is preserved. + +### Conclusion: +Gerd and Michael will intiate a draft pull request to the branch. + +## General concept of how to convey information from the original model +### Conclusion: +Gerd and Michael: Pull request to discuss this topic, including the idea of annotations. + +## Keep track of items +### Conclusions:: +Henrik: Create a check list in the ReadMe.md diff --git a/RationaleMCP/0031/MoM/2019-11-22_FlatModelica_Web-Meeting.md b/RationaleMCP/0031/MoM/2019-11-22_FlatModelica_Web-Meeting.md new file mode 100644 index 000000000..ad44b7f9b --- /dev/null +++ b/RationaleMCP/0031/MoM/2019-11-22_FlatModelica_Web-Meeting.md @@ -0,0 +1,46 @@ +Meeting Date: 10.10.2019 09:00 Location: Skype Meeting +# Participants + + Henrik Tildefeit (Wolfram) + Kai Werther (ETAS) + Gerd Kurzbach (ESI-ITI) + Christoff Bürger (DS) + Hans Olsson (DS) + Martin Sjölund (LiU) + Francesco Casella (Politecnico di Milano) + Giovanni Agosta (Politecnico di Milano) + Stefano Cherubin (Politecnico di Milano) + Terraneo (Politecnico di Milano) + Oliver Lenord (Bosch) + +# Notes + +## Introduction +Brief introduction of all participants +Main interest of Politecnico di Milano is to work on a research project for a high performance compiler for large scale models. + +Summary of the current work, documents on github and status of discussion by Henrik. + +## Questions: +Franscesco: Non-trivial issue with fluid models are arrays and records +Henrik: We will need arrays and records but its up to the tools to decide on the level of scalarization. Flat may be understood as fully scalarized, but in +Francesco + +How to start the discussion? +black listing vs. white listing +Approach: black list the obvious, white list what is needed for eFMI EqCode, have discussion threads for all items that are contorversial. +Follow the outlined scheme: Principles for use of language constructs vs annotations. +Post pone this topic to the next meeting as Gerd has to leave. + + +## Express hybrid DAE +Kai: SCODE-CONGRA has a separate switching logic appart from the continuous equations. Will spend some thought on this unti next time. + +## To Dos until next meeting +Split the current oull request into smaller chunks: +* Create an annotations.md file and linking it to the list. [Henrik] +* Comment on this pull request [Gerd + all] +* Discuss and decide this at the next meeting. + +## Next Meeting +Friday 6th, 11.00AM diff --git a/RationaleMCP/0031/ReadMe.md b/RationaleMCP/0031/ReadMe.md new file mode 100644 index 000000000..ff769d3ab --- /dev/null +++ b/RationaleMCP/0031/ReadMe.md @@ -0,0 +1,118 @@ +# Modelica Change Proposal MCP-0031
Flat Modelica and MLS modularization +Peter Harman, Werther Kai, Gerd Kurzbach, Oliver Lenord, Hans Olsson, Michael Schellenberger, Martin Sjölund, Henrik Tidefelt + +**(In Development)** + +## Summary +This MCP is a new attempt at introducing a specification of an intermediate format which will be called _Flat Modelica_. + +### In a sentence (or two) +Flat Modelica is a language to describe hybrid (continuous and discrete) systems with emphasis on defining the dynamic behavior. It is an integral part of the Modelica specification, not a new separate standard. + +### Use cases +Use cases to have in mind in the design of Flat Modelica, also indicating the usefullness of the Flat Modelica endeavor: +* 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. + - 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. +* 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 flattened models. +* Comparison of different Modelica back ends with the same flattened model. +* Plant model descriptions for use in control design. +* Equation analysis for fault detection and isolation. +* Integration with third party tools for equation analysis. _(Could we be more specific about what this migh be?)_ +* Platform for academic research on dynamic systems. For example, numeric methods. +* Target language for new high level modeling languages. +* IP protection when combined with obfuscation. + +### Relation to eFMI +One of the key use cases driving the development of Flat Modelica is its use as basis for the _Equation Code_ of [eFMI](https://itea3.org/index.php/project/emphysis.html). The requirements for eFMI are much smaller in terms of language features compared to the needs for serving as intermediate representation in the Modelica standard. To accommodate both use cases, the Equation Code of eFMI will be defined as a restricted variant of Flat Modelica. + +### Requirements +From the use cases above, some implicit requirements follow: +* Simple enough to be attractive for applications that essentially just want a simple description of variables and equations, meaning that many of the complicated high level constructs of Modelica are removed. +* Expressive enough to allow the high level constructs of Modelica to be reduced to Flat Modelica without loss of semantics. +* When Flat Modelica serves as an intermediate representation of the translation of a higher level language (such as Modelica), errors detected in Flat Modelica code shall be traceable to the original code. +* Human readable and writeable, since not all use cases assume Flat Modelica being produced from a higher level language by a tool. + +## Roadmap +Due to the large size of this MCP, it has been necessary to break it down into smaller subtopics. Some of these may will be complicated enough to require their own discussion threads (in the form of pull requeststs to the MCP branch), while other may be resolved more easily during meetings and be implemented directly on the MCP branch. + +### Flat Modelica 0.1 (this MCP) +These are subtopics that are considered necessary to resolve for a first version of Flat Modelica. By keeping this list short, increase chances of ever getting to the release of a first version. +- [x] Flat Modelica identifier naming scheme. +- [x] Principles for use of language constructs vs annotations. [Design](annotations.md), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2459) +- [x] Get rid of the obviously irrelevant parts of the grammar. [Design](grammar.md), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2465) +- [x] Get rid of `connect` equations. +- [x] Get rid of conditional components. (The _condition-attribute_ is still present in grammar.) [Design](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Bconditional-component/RationaleMCP/0031/differences.md#conditional-components), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/3149) +- [x] Settle the top level structure. [Design](grammar.md#start-rule), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2469) +- [x] List of supported built-in operators and functions. [Design](functions.md), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2513) +- [x] Get rid of unbalanced `if`-equations. [Design](differences.md#unbalanced-if-equations) +- [x] Restrict constant expressions for translation time evaluation. [Design](differences.md#constant-expressions), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2473) +- [x] Handle array dimensions with parameter variability. [Design](differences.md#array-size), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2471) +- [x] Decide on just one way to specify array dimensions. [Design in progress](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Bdimension-declaration/RationaleMCP/0031/grammar.md), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2468) +- [x] Define allowed forms of type aliases (related to _one way to specify array dimensions_). [Design in progress](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Btype-aliases/RationaleMCP/0031/type-aliases.md), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2555) +- [x] Allowing array subscripting on general expressions. [Design in progress](https://github.com/modelica/ModelicaSpecification/pull/2540/commits/b5eab9d5edcab8766a79637292be6a1e68b2bacc#diff-069d28cf3b6b78debdcada80b99b6c0b), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2540) +- [x] Get rid of `protected`. [Design in progress](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031-protected/RationaleMCP/0031/grammar.md#b22-class-definition), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/3162) +- [x] Investigate need for `final`. [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2994) +- [x] Origin of modifications (for start value prioritization). [Design in progress](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Bstart-value-prioritization/RationaleMCP/0031/differences.md#guess-value-prioritization), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2997) +- [x] Get rid of `false` as default for `fixed`. [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2996) +- [x] Restricted rules for use of `start` attribute for parameter initialization. [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2995) +- [x] Get rid of record member variability prefixes `constant` and `parameter`. [Design in progress](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Brecord-member-variability/RationaleMCP/0031/differences.md#variability-in-record-member-declaration), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2694) (Not gone, but restricted.) +- [x] Simplify modifications. [Design in progress](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2BModifications/RationaleMCP/0031/differences.md#simplify-modifications), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2558) (Remaining parts covered by other items.) +- [x] Simplify value modifications. [Design in progress](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Bvalue-modification/RationaleMCP/0031/differences.md#Value-modification), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2747) +- [x] Simplify record construction and function default arguments. [Design in progress](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Brecord-construction/RationaleMCP/0031/differences.md#function-default-arguments), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/3038) +- [x] Express final modification. [Design in progress](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Bfinal-modification/RationaleMCP/0031/differences.md#Final-modification), [PR with discussion](https://github.com/modelica/ModelicaSpecification/pull/2748) +- [x] Make constants available to types. [Design](https://github.com/modelica/ModelicaSpecification/blob/MCP/0031%2Btop-level-structure/RationaleMCP/0031/grammar.md#Start-rule), [PR (closed?!)](https://github.com/modelica/ModelicaSpecification/pull/2746) +- [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 _Flat Modelica_), considering that scalarization isn't mandatory. + +### Flat Modelica 0.1+…1.0 (future MCPs) +In future minor versions of Flat Modelica 1, we could improve the language by incorporating smaller improvements that were not considered necessary for version 1.0. +- [ ] Handling of parameters treated as constants. [Previous discussion](https://github.com/modelica/ModelicaSpecification/pull/3161) +- [ ] Primitive operations for triggering of events, to which the current event generating functions can be reduced. +- [ ] Get rid of function calls with named arguments. +- [ ] Get rid of function argument defaults. +- [ ] Get rid of higher order functions. +- [ ] Get rid of record constructors. +- [ ] Allow to identify connector variables. +- [ ] Handle equations and algorithms derived from arrays of components efficiently. +- [ ] Reintroduce `each` for efficiency and to avoid code duplication. +- [ ] Figure out what to do with Connectors and FMI3.0 Terminals. + +### Flat Modelica 2.0 (future MCPs) +Big changes that don't make sense to even consider for a minor release of version 1 are listed here. Being listed here shall not be interpreted as even being likely to ever happen; this is just a collection of all the ideas that don't fit in the more realistic roadmap for version 1. +- [ ] Allowing some simple form of `model` that makes it possible to preserve structure of the equations that will allow more efficient symbolic processing and production of executables of much smaller size. + +## Revisions +| Date | Description | +| --- | --- | +| 2019-01-09 | Henrik Tidefelt. Filling this document with initial content. | + +## Contributor License Agreement +All authors of this MCP or their organizations have signed the "Modelica Contributor License Agreement". + +## Rationale +The requirements on what Flat Modelica should and shoudn't be are currently being developed in a [separate document](Flat-Modelica-requirements.md). + +## Backwards Compatibility +It is the goal of this MCP that it should only change the way the Modelica language is described, not either adding, removing, or changing any of the Modelica language features. Hence, it should be completely backwards compatible. + +## Tool Implementation +While existing Modelica implementations should work just as well before as after incorporation of this MCP, there should still be a proof of concept implementation showing how Flat Modelica can be produced by a tool, and that the Flat Modelica output can then be used as input to a Modelica back end for simulation. Ideally, this should be demonstrated using different tools for the two tasks. + +### Experience with Prototype +(None, so far.) + +## Required Patents +To the best of our knowledge, there are no patents that would conflict with the incorporation of this MCP. + +## References diff --git a/RationaleMCP/0031/annotations.md b/RationaleMCP/0031/annotations.md new file mode 100644 index 000000000..d43d95368 --- /dev/null +++ b/RationaleMCP/0031/annotations.md @@ -0,0 +1,109 @@ +# Annotations + + +## Use of annotations vs attributes + +This section describes the design question of when to use an annotation and when to use a first class attribute in the language. + +### Design guidelines + +The following guideline shall be applied when determining whether to use an annotation to convey information in Flat Modelica: +- Information kept in annotations in Modelica shall remain annotations in Flat Modelica. + +There are no design guidelines for how to convey information needed for Flat Modelica when there is no counterpart in Modelica. Design decisions will be have to be made case by case. + +### Rationale + +With the flexible structure of annoations, it is possible to use annotations for anything that could also have been a first class attribute in the language. These are the main arguments for and against the proposed guidelines versus a restriction not not use annotations for anything that may impact symbolic or numeric processing: +- (+) Some Modelica users would be suprised to find out if something like `GenerateEvents = true` (which is an annotation in Modelica) wasn't an annotation in Flat Modelica. +- (+) Disregarding annotations allows users non-Modelica background to not be put off by things such as `LateInline` that they don't have an intuition for. +- (-) Seeing the annotations that impact the simulation result among all other information stored in annotations will require tool support beyond simply collapsing all annotations. +- (-) The (overly) flexible structure of annoatations makes it harder to detect mistakes, whereas the exact syntax of primitive language constructs could have been expressed in the language grammar. + + +## Summary of Flat Modelica annotations + +These are all the non-vendor specific annotations inherited from full Modelica that may influence the code generation process: +- `Inline` — Applied to a function, indicates it should be inlined +- `LateInline` — Applied to a function, indicates it should be inlined after symbolic transformations have been performed +- `InlineAfterIndexReduction` — Applied to a function, indicates it should be inlined after differentiation for index reduction and before other symbolic transformations are applied +- `GenerateEvents` — Applied to a function, indicates that zero crossing functions inside the function algorithm should generate events (e.g. by inlining the function) +- `smoothOrder` — Applied to a function, indicates the function is N times continuously differentiable, allowing it to be symbolically differentiated +- `derivative` — Applied to a function, points to the total derivative function +- `inverse` — Applied to a function, points to the inverse function + +These are all the non-vendor specific annotations inherited from full Modelica that are relevant for parameter input and simulation output: +- `HideResult` — Applied to a parameter or variable, implies the variable should not be included in the simulation output +- `choices` — Applied to a parameter or variable, can be used to enumerate and tag different values for parameter input + +These are the new annotations introduced in Flat Modelica, each explained in more detail below: +- [`Protected`](#protected) — Indicate whether component declaration comes from protected section in original full Modelica model + + +### `Protected` + +Form: +``` +Boolean protected = false; +``` + +The `Protected` annotation is only allowed on a non-function component declaration. + +The annotation does not come with any hard semantics, but can be useful for things such as: +- equation system tearing heuristics +- guiding which variable name to keep when performing alias elimination +- criterion for selection of which variables to include in the simulation result stored to file + +When the Flat Modelica is generated from a full Modelica model, a component coming from a protected section (except inside functions) in the full Modelica model shall have the annotation `Protected = true` in Flat Modelica. +A component coming from a public section must not have the `Protected = true` annotation. + +When the Flat Modelica is not generated from a full Modelica model, the `protected` annotation needs to be understood by comparison to the case of full Modelica origin. + +For example, consider the following full Modelica model: +``` +model M +protected + parameter Real p = 1.0; +end M; +``` + +Note that `p` can only be modified when extending the model, and that the `Protected = true` annotation does not enforce this constraint in Flat Modelica. +Hence, the correct conversion to Flat Modelica needs to combine `Protected = true` with treatment similar to a parameter declared `final`: +``` +model 'M' + parameter Real 'p' annotation(Protected = true); +initial equation + 'p' = 1.0; /* From full Modelica protected declaration equation. */ +end 'M'; +``` + +Note that in full Modelica a protected section can occur at any level of the component hierarchy, as shown by the following example: +``` +model M + Real x; +protected + Real p; +end M; + +model N + M m1; +protected + M m2; +end N; +``` +In Flat Modelica, the level of the original protected section is lost: +``` +package 'M' + model 'M' + Real 'm1.x'; + Real 'm1.p' annotation(Protected = true); + Real 'm2.x' annotation(Protected = true); + Real 'm2.p' annotation(Protected = true); + end 'M'; +end 'M'; +``` + + +## Vendor annotations + +Flat Modelica allows for vendor-specific annoations in the same way as in full Modelica. diff --git a/RationaleMCP/0031/constsize.md b/RationaleMCP/0031/constsize.md new file mode 100644 index 000000000..d2fed44fa --- /dev/null +++ b/RationaleMCP/0031/constsize.md @@ -0,0 +1,110 @@ +# The `constsize` expression rationale +The `constsize` expression is introduced and defined in [differences.md](differences.md#the-constsize-expression). This document provides background that may be helpful for understanding the current design, as well as points out possible directions for future developments of `constsize` that could be useful in case it turns out that the current design doesn't solve all the problems it is faced with when applied to actual Modelica test cases. + +## Leaving leading sizes flexible +This section describes a limitation of the current design, and ways in which it can be remedied in the future. + +### Limitation +The current forms of `constsize` don't allow leaving some leading sizes flexible, while making other sizes constant. It means that some full Modelica function algorithms cannot be directly converted to Flat Modelica: +``` +function flexibleTrouble +protected + Real[:, :] a; /* (That the second dimension doesn't have constant size matching 'b' is probably a sign of bad design.) */ + Real[2] b; + Real[:] y; +algorithm + /* Allowed in full Modelica, but not in Flat Modelica. + * Size mismatch in array multiplication: ':' is not compatible with constant size. + */ + y := a * b; + /* Trying to address Flat Modelica type error. + * Well-typed, but will generally fail at runtime, as 42 has nothing to do with the first size of 'a'. + */ + y := constsize(a, 42, 2) * b; +end flexibleTrouble; +``` + +### Remedies + +To remedy this, one could also add a third form where two constant variability `Integer` arrays of equal size are given, where the first array specifies dimensions, and the second array specifies the corresponding sizes: +``` +constsize(arrExp, {d1, d2, ..., dn}, {sd1, sd2, ..., sdn}) +``` + +Alternatively, one could make `constsize` a keyword and change the grammar to also allow `:` instead of a constant `Integer` (possibly causing some confusion for human readers that don't understand why a `:` is allowed in a place that looks like a function call argument): +``` +constsize(arrExp, :, s2, ..., sn) +``` + +For now, however, we don't expect this to be a problem for real-world examples. Regarding the example given above, the variable `a` should probably have been declared as `Real[:, 2]` to start with, and then the matrix multiplication would be fine also in Flat Modelica. For this reason, and knowing that there are ways that the current `constsize` expressions can be generalized in the future, we leave the design of `constsize` for now with this known limitation. + +With a way to specify `:` in the `constsize` expression, one would have to define if a `:` specified for a constant dimension should turn that dimension into a flexible size, or if it should be defined as leaving the size constant. This could be particularly useful when dealing with dimensions indexed by enumerations: +``` +model EnumerationCardinality + function f + output Real[:, MyEnumType, :] y; + ... + end f; + function h + protected + Real[:, MyEnumType, 3] z; + algorithm + /* Cumbersome way to assign the result of f() to z: + */ + z := constsize(f(), :, size(z, 2), 3); /* OK. */ + /* Trying to just use size(z) doesn't work because size(z) has non-constant + * variability due to the flexible size of the first dimension: + */ + z := constsize(f(), size(z)); /* Error. */ + /* Possible interpretations of specifying a ':' size where the expression + * type has constant size (here given by the cardinality of MyEnumType): + * - If the resulting size is still the original constant size, this + * is a convenient way to avoid need to figure out cardinality. + * - If the resulting size is flexible, it will be a type error to assign + * to 'z' where the size needs to be constant. + */ + z := constsize(f(), :, :, 3); /* Convenient or error depending on design. */ + end h; +end EnumerationCardinality; +``` + +As long as there are no known use cases for turning a constant size into flexible using a `constsize` expression, choosing the design where a `:` doesn't turn a constant size into flexible seems attractive. However, more experience is needed in order to determine the need for turning constant sizes into flexible. It should be noted that turning constant into flexible is rather easy even without `constsize` inside a function; all one has to do is assign to a helper variable where the dimension in question has flexible size. + +Finally, note that a very cumbersome workaround that doesn't require generalization of `constsize` is to introduce a new function to do the job: +``` +function _constsize_Real_flexible_2 + input Real[:, :] x; + output Real[size(x, 1), 2] y; +algorithm + assert(size(x, 2) == 2, "Expected second array dimension to have size 2."); + for i in 1 : size(y, 1) loop + for j in 1 : size(y, 2) loop + y[i, j] := x[i, j]; + end for; + end for; +end _constsize_Real_flexible_2; +``` + +## Alternative syntax +Another syntax for `constsize` was also considered to make it easier to address the limitation of not being able to leave some leading sizes flexible. Here, a `constsize` expression uses special syntax instead of taking the form of a normal function call: +``` +constsize[s1, s2, ..., sn](arrExp) /* "Square bracket form" reminding of component declaration. */ +constsize(dimsExp)(arrExp) /* dimsExp is a constant Integer array expression, such as size(...). */ +``` + +Pros and cons leading to the current decision of going with the function call syntax instead: +- Syntax (square bracket form) is easily and naturally extended to also allow `:` sizes where the resulting size must not be constant (the use of `:` in a list of sizes given between square brackets is already established). +- Syntax (square bracket form) is easily and naturally extended to allow non-`Integer` sizes to be given as declared, rather than as the integer measuring the size of the dimension. That is, to allow `Boolean` instead of the integer `2`. +- Not using function call syntax means grammar has to be extended (with `constsize` as Flat Modelica keyword), without added value until generalized to also allow `:`. +- The currently proposed function call syntax would also be possible to extend, see above. + +Example (compare [example using current design](differences.md#the-constsize-expression)): +``` +model M + function f + output Real[Boolean, :] y; + ... + end f; + Real[Boolean, 3] b = constsize[Boolean, 3](f()); /* */ +end M; +``` diff --git a/RationaleMCP/0031/differences.md b/RationaleMCP/0031/differences.md new file mode 100644 index 000000000..ae167eb71 --- /dev/null +++ b/RationaleMCP/0031/differences.md @@ -0,0 +1,1182 @@ +# 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. + + +## Top level structure + +The top level structure (see [grammar](grammar.md#Start-rule)) of a Flat Modelica description can have several top level definitions, with a mandatory `model` definition at the end. +The definitions before the `model` either define types or global constants. + + +## Lexical scoping and record definitions + +Lookup in Flat Modelica is significantly simplified compared to full Modelica due to the restricted top level structure of a Flat Modelica program, but there are two more restrictions on top of that explained in this section. + +Taken together, the two restrictions can be summarized concisely as follows: +- In Flat Modelica, a member of a record can only be accessed through an instance of the record. + (This can also be described in terms of lexical look-up rules.) + +### No package constant access for records + +Flat Modelica – unlike Full Modelica — doesn't allow a record to be treated as a package for purposes of lookup just because it satisfies the package restrictions. +For example, this is illegal: +``` +package 'RecordIsNotPackage' + record 'R' "Record fulfilling the requirements of a package" + constant Real 'c' = 1.5; + end 'R'; + model 'RecordIsNotPackage' + Real 'x' = 'R'.'c'; /* Error: Illegal attempt to access member 'c' inside definition of record 'R'. */ + end 'RecordIsNotPackage'; +end 'RecordIsNotPackage'; +``` + +### Inside record definitions + +Inside a record definition, members of the same record are not in scope. + +For example, this is illegal: +``` +package 'OutOfScope' + record 'R' + constant Integer 'n'; + Real['n'] 'x'; /* Error: Unknown variable 'n'. */ + parameter Real 'p'; + Real 'y'(start = 'p'); /* Error: Unknown variable 'p'. */ + end 'R'; + model 'OutOfScope' + 'R' 'r'('n' = 3); + end 'OutOfScope'; +end 'OutOfScope'; +``` +Instead, constants may need to be evaluated and modifications moved to the model component declarations: +``` +package 'OutOfScope' + record _R1 "Automatically generated specialization of R(n = 3)" + constant Integer 'n' = 3; + Real[3] 'x'; + parameter Real 'p'; + Real 'y'; + end _R1; + model 'OutOfScope' + _R1 'r'('y'(start = 'r'.'p')); + end 'OutOfScope'; +end 'OutOfScope'; +``` + +One of the sought effects of this restriction is that all only constant modifications can be expressed in Flat Modelica type definitions, greatly simplifying reasoning about types and their representation in tools. + + +## Unbalanced if-equations + +In Flat Modelica, all branches of an `if`-equation must have the same equation size. +Absence of an else branch is equivalent to having an empty else branch with equation size 0. + +An `if`-equation without `else` is useful for a conditional `assert` and similar checks. + +Note: _The "equation size" count the number of equations as if the equations were expanded into scalar equations, +but does not require that the equations can be expanded in this way._ + +### Change and reason for the change +In Modelica this restriction only applies for `if`-equations with non-parameter conditions. +For `if`-equations with parameter condition it does not hold, and if the equation sizes +differ those parameters have to be evaluated. In practice it can be complicated to separate those cases, +and some tools attempt to evaluate the parameters even if the branches have the same equation size. + +Flat 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 Flat Modelica. + + +## Conditional components + +Flat Modelica does not have conditional components (see `condition-attribute` in the [grammar](grammar.md)). +All checks that apply to inactivated components in Full Modelica will need to be checked while generating Flat Modelica. + +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 Flat Modelica. + + +## Pure Modelica functions + +In addition to full Modelica's classification into _pure_ and _impure_, Flat Modelica adds the concept of a `pure constant` function, informally characterized by the following properties: +- Only the output values of a function call influence the simulation result (considered free of side effects for purposes of program analysis). +- The function itself only contributes with `constant` variability to expressions where it is called. That is, when the function is called with constant arguments, the result is assumed to be the same when evaluated at translation time and when evaluated at any point during simulation. +- It is straight-forward to evaluate a call to a `pure constant` function at translation time. + +The built-in functions are `pure constant`, and a user-defined pure `function` or `operator function` can be declared as `pure constant` by adding `constant` in the class prefix right after `pure`. For example: +``` +pure constant function add1 +``` + +The implementation of a `pure constant` functions is more restricted than that of pure functions in general: +- It may not have `external` implementation. +- It may not contain any `pure(…)` expression. +- All called functions must be `pure constant`. + +The rule for `pure(impureFunctionCall(...))` needs to be rephrased to not say _allows calling impure functions in any pure context_, since the body of a `pure constant` function is also a pure context. Perhaps something like this instead: +> meaning that the present occurrence of `impureFunctionCall` should be considered pure (not `pure constant`) for purposes of purity analysis. + +### Reason for change + +This change was made to support the [changed definitions of _constant expression_](#Constant-expressions). + + +## Function default arguments + +Flat Modelica functions cannot have function default arguments. +A tool producing Flat Modelica from full Modelica can accommodate this by automatically generating a helper function for every present subset of arguments used in the model. +(The helpers can be omitted if the defaults are literal or otherwise independent of the other inputs, since those values can be added in each call. +No helper function needs to be created for argument combinations that aren't used in the model, which means that the potential combinatorial explosion of possible argument combinations is avoided.) + +For example, consider this full Modelica model: +``` +model M + function f + input Real a; + input Real b = a + 1; + input Real c = 2 * b; + output Real y = a + b + c; + end f; + + Real x = f(0.5, c = time); +end M; +``` +Here, there is only one call to the function `f` making use of argument defaults. +Hence, out of the three possible combinations of absent arguments (not counting all arguments being present, as this will correspond to the base variant of `f` in Flat Modelica), only one needs a helper function in Flat Modelica: +``` +package 'M' + function 'M.f' + input Real 'a'; + input Real 'b'; + input Real 'c'; + output Real 'y' = 'a' + 'b' + 'c'; + end 'M.f'; + + function '-M.f:1,3' "Automatically generated helper for passing only the first and third arguments to 'M.f'" + input Real 'a'; + input Real 'c'; + Real 'b' = 'a' + 1; + output Real 'y' = 'M.f'('a', 'b', 'c'); + end '-M.f:1,3'; + + model 'M' + Real 'x' = '-M.f:1,3'(0.5, time); + end 'M'; +end 'M'; +``` + +Note the name chosen for the automatically generated helper, `'-M.f:1,3'`. +Due to the leading hyphen, it belongs to the part of the variable namespace that is available for automatically generated names, meaning that there is no risk of collision with names coming from the original full Modelica source. + +A Flat Modelica function may still have declaration equations on its inputs, but unlike full Modelica, these are ignored. +They are only allowed for the sake of consistency with how deeper value modifiers on functions inputs are handled, see [record construction](#record-construction). +``` +function 'f' + input Real 'a'; + input Real 'b' = 'a' + 1; /* No default; declaration equation is ignored in Flat Modelica. */ + input Real 'c' = 2 * 'b'; /* No default; declaration equation is ignored in Flat Modelica. */ + output Real 'y' = 'a' + 'b' + 'c'; /* Declaration equations are useful for outputs and local variables. */ +end 'f'; +``` + + +## Records + +### Record construction + +Unlike full Modelica, there are no implicitly defined record constructor functions in Flat Modelica. +A tool producing Flat Modelica from full Modelica can accommodate this by automatically generating helper functions as needed. +The default arguments of the full Modelica record constructor can be handled just like default arguments of other functions, see [above](#function-default-arguments). +In addition to the helper functions for handling default argumnets, tools producing Flat Modelica from full Modelica can also create a base function for record construction based on values for all record members. + +For example, consider this full Modelica model: +``` +model M + record R + Real a; + Real b = 1; + Real c = a + 1; + end R; + + R r = R(3); +end M; +``` +To convert this to Flat Modelica, a tool can automatically create two functions: +``` +package 'M' + record 'M.R' + Real 'a'; + Real 'b'; + Real 'c'; + end 'M.R'; + + function '-M.R' "Automatically generated constructor for 'M.R'" + input Real 'a'; + input Real 'b'; + input Real 'c'; + output 'M.R' _result('a' = 'a', 'b' = 'b', 'c' = 'c'); + end '-M.R'; + + function '-M.R:1' "Automatically generated helper for passing only the first argument to '-M.R'" + input Real 'a'; + Real 'b' = 1; + Real 'c' = 'a' + 1; + output 'M.R' _result = '-M.R'('a', 'b', 'c'); + end '-M.R:1'; + + model 'M' + 'M.R' 'r' = '-M.R:1'(3); + end 'M'; +end 'M'; +``` + +### Record member value modifications + +Even though Flat Modelica doesn't come with implicitly defined record constructor functions — that in full Modelica are derived based on value modifications in the record type definition – it is still allowed to have value modifications for the members of a record type in Flat Modelica. +Note that the top level structure of a Flat Modelica model ensures that the value modifications that are part of record types can only contain constant values (possibly obtained by evaluation of constant expressions). +As usual, such value modifications can be overridden when declaring a component of the record type, and when made in a model component declaration, it is possible to also have non-constant expressions in the modifications. +The only semantics of value modifications in record types is that they will be used as the basis for the effective modifications of a component declaration, but the semantics of value modifications in a component declaration are different depending on the kind of component declaration (function/record/model component declaration). + +#### Record component declarations + +Value modifications on a record component declaration are simply stored as part of the record type being defined. +All modifications must be given by constant expressions, allowing them to be evaluated. + +For example: +``` +record 'R' + Real 'x' = 1; /* Constant value is stored as part of the type 'R'. */ +end 'R'; + +record 'S' + 'R' 'r1'; /* Keeps modification from component type. */ + 'R' 'r2'('x' = 2); /* Overrides modification from component type. */ +end 'S'; +``` + +Note that in the example above, a tool's internal representation of the record type 'S' does not need to remember the origin of the modifications; it is sufficient to just remember that the modification of `'r1'.'x'` is `1`, and that the modification of `'r2'.'x'` is `2`. + +#### Model component declarations + +A value modification in a model component declaration is equivalent to having a normal equation for the record member. + +Example (omitting the `package` wrapper for brevity): +``` +record 'R' + Real 'x'; + Real 'y' = 1; +end 'R'; + +function 'makeR' + output 'R' 'r'('x' = 2); +end 'makeR' + +model 'M' + 'R' 'r1'; + 'R' 'r2'; + 'R' 'r3' = 'makeR'(); /* OK: Declaration equation removes all modifications. */ +equation + 'r1'.'x' = 2; /* OK, making 'r1' fully determined. */ + 'r2' = 'makeR'(); /* Error: 'r2'.'y' becomes overdetermined. */ +end 'M' +``` + +#### Function input component declarations + +All value modifications of a function input component declaration are ignored. + +For example, this is valid: +``` +record 'R' + Real x; +end 'R'; + +function 'foo' + input 'R' 'u'('x' = 10); /* Value modification is ignored. */ + output Real 'y' = 'u'.'x'; /* Completely determined by record passed to function. */ +end 'foo'; +``` + +It follows that record types containing value modifications are usable in function input component declarations, but that the value modifications do not make any difference here. + +Note the analogy between the example above and the following: +``` +pure function 'makeR10' "Pure constant function returning value of type 'R'" + output 'R' 'r'('x' = 10); +end 'makeR10'; + +function 'foo' + input 'R' 'u' = 'makeR10'(); /* Not a default; declaration equation is ignored. */ + output Real 'y' = 'u'.'x'; /* Completely determined by record passed to function. */ +end 'foo'; +``` + +#### Function output and local component declarations + +For output and local (that is, neither input nor output) function component declarations the value modifications are used for initial assignments, and are then ignored during the remaining part of the function call evaluation. + +For example: +``` +record 'R' + Real 'x'; + Real 'y'; +end 'R'; + +function 'f' + output 'R' 'result'('y' = 5); /* Assigning initial value to 'result'.'y'. */ +algorithm + 'result'.'x' = 6; +end 'f'; +``` + +For the purpose of analyzing uninitialized use of variables in functions, a record variable is considered assigned when all it's members have been assigned. +(A record-valued assignment to the entire variable is a special case of this, with all record members being assigned at once.) +It follows that a function output (of record type) can be completely determined using only modifications in the component declaration. + +Note that one consequence of the initial assignment semantics is that this is valid: +``` +record 'R' + Real 'x' = 1; +end 'R'; + +function 'f' + output 'R' 'result'; /* Hidden initial assignment to 'result'.'x', making 'result' fully assigned. */ +end 'f'; +``` + + +## Variability of expressions + +### Constant expressions +In Flat Modelica, a _constant expression_ is more restricted than in full Modelica, by adding the following requirement: +- Functions called in a constant expression must be `pure constant`. + +By requiring functions called in a constant expression to be `pure constant`, it is ensured that a constant expression can always be evaluated to a value at translation time. A function call that is not a constant expression must not be evaluated before simulation starts. + +### Parameter expressions + +In Flat Modelica, a _parameter expression_ is more restricted than in full Modelica, by adding the following requirement: +- Functions called in a parameter expression must be pure. + +As a consequence, the full Modelica syntactic sugar of using an impure function in the binding equation of a parameter is not allowed in Flat Modelica. Such initialization has to be expressed explicitly using an initial equation. Hence, the rules of variability hold without exception also in the case of components declared as parameter. + +### Reason for change +By excluding `external` functions, translation time evaluation of constant expressions is greatly simplified. By excluding `impure` functions and `pure(…)` expressions, it is ensured that it doesn't matter whether evaluation happens at translation time or at simulation (initialization) time. + +Forbidding translation time evaluation of function calls in non-constant expressions generalizes the current Modelica rule for `impure` functions and makes it clear that this is not allowed regardless whether this is seen as an optimization or not. (The current Modelica specification only has a non-normative paragraph saying that performing optimizations is not allowd.) + +The change regarding parameter expressions could be extended to discrete-time expressions as well without loss of expressiveness due to the existing restrictions on where an impure function may be called. This could also be expressed more generally by saying that a function call expression where the callee is impure is a non-discrete-time expression. However, it was decided to not include this in the formal description of differences between Modelica and Flat Modelica in order to avoid describing changes that only clarify things without actually making a difference to semantics. + +The shifts in variability of function calls could be summarized as _the variability of a function call expression is the highest variability among the argument expressions and the variability of the called function itself_, where the _variability of a function_ is defined by the following table: + +| Function classification | Function variability | +| --- | --- | +| pure constant | constant | +| pure, otherwise | parameter | +| impure | continuous-time | + +Seen this way, the rules about which functions may be called in the body of a function definition ends up being another case of variability enforcement. + +This covers what one can currently express in full Modelica. In the future, one might also introduce _pure discrete_ functions that don't have side effects, but that must be re-evaluated at events, even if the arguments are constant. + + +## Variability specification inside types + +In a record definition it is possible to have variability prefixes (`parameter` or `constant`) on the record member component declarations. This results in a type containing variability specification, referred to as a _variability-constrained type_. Unless clear from context, types that are not variability-constrained should be referred to as _variability-free types_. + +Variability-constrained types may only be used in the following restrictive ways: +- Definition of new types (that will also be variability-constrained). +- Model component declaration. +- A (sub-) expression of variability-constrained type (such as a reference to a model component declared with such type) may only be used in one of the following ways: + - Component references. That is, accessing a member of a record, application of array subscripts, and combinations thereof. Note that the resulting expression might again be of variability-constrained type, in which case these restrictions must also be met recursively. + - Passing as argument to a function. It is possible for a variability-constrained record member to be received by a record member without variability constraint, and structural subtyping also allows the receiving record type to not have members corresponding to the variability-constrained members of the passed record. + - Alone on one side of an equation in solved form, or as left hand side of an assignment statement. In this case a variability-constrained record member (at any depth) that does not have a corresponding record member on the other side of the equation (right side of assignment) is not considered part of the equation or assignment. + +The restrictions above should be considered preliminary, as we have yet to find out in more details how these restrictions can be met in real world applications. + +It is expected that the restrictions on variability-constrained types will sometime require a type to exist in both a variability-constrained and variability-free variant. It remains to find out whether the most useful variability-free variants are such that the variability-constrained members of records have been removed, or such that just the variability-constraints have been removed. + +The last of the ways that an expression of variability-constrained type may be used – that is, in a solved equation or assignment – is an extension of full Modelica that is provided to mitigate potential problems caused by needing to have two Flat Modelica variants of the same full Modelica type. + +A [separate document](variability-constrained-types.md) gives examples of how variability-constrained types may arise in Flat Modelica, and how their constraints can be handled. + + +## Array size + +### Array types +In Flat Modelica, array size is part of an array type. Each dimension has a size that is either _constant_ or _flexible_. A constant size is one that is an `Integer` number (of constant variability). It follows that array dimensions index by `Boolean` or enumeration types have constant size (TODO: figure out correct terminology). A flexible size is an `Integer` of non-constant variability, that is, it corresponds to an array dimension indexed by `Integer` and where the upper bound is a number unknown to the type system. + +When determining expression types, every array dimension must be unambiguously typed as either constant or flexible. Where there are constraints on array sizes, for instance when adding two arrays, a flexible array size is only compatible with another flexible array size. It is a runtime error if the two flexible array sizes are found to be incompatible at runtime (a tool can optimize runtime checks away if it can prove that the sizes will be compatible). + +In an array equation, the array type must have constant sizes. On the other hand, a flexible size on the left hand size of an array assignment does not impose any size constraint on the right hand size, and is allowed. + +When determining the type of a function call, the sizes of output array variables are determined based on the input expressions and the function's declarations of input and output components. An output array size is constant (for the function call at hand) if it can be determined based on the types of input expressions and the constant variability values of input expressions. When an array size cannot be determined based on this information (including the trivial case of a function output component declared with `:` size), the size is flexible. + +Example: +``` +model M + function f + input Integer n; + input Real[:] x; + output Real[n + size(x, 1)] y; + protected + Real[:] a; + algorithm + a := {0.5}; /* OK: Constant size can be assigned to flexible size. */ + ... + end f; + parameter Integer p = 2; + constant Integer c = 3; + Real[2] a = fill(1.0, p); /* Error: expression has flexible size. */ + Real[5] b = f(c, a); /* OK. */ +end M; +``` + +#### Change and reason for the change +This draws a clear line between the constant and flexible array sizes. This is important for portability (lack of clear separation opens up for different interpretations, where what is considered valid code in one tool is considered a type error in another). The clear separation also means that flexible array size becomes an isolated Flat Modelica language feature that can be easily defined as an unsupported feature in eFMI. + +To only consider a flexible size compatible with another flexible size is a restriction that may be removed in the future, allowing different forms of inference on the array sizes. For now, however, such inference is not considered necessary for a first version of Flat Modelica, and defining such inference would also involve too much work at this stage of Flat Modelica development. + +It is believed that the clear separation of constant and flexible array sizes is also a necessary starting point for a future extension to allow components with flexible array size outside functions. + +### The `constsize` expression +A new `constsize` expression is introduced for making assertions on array sizes. A limitation of the current design, and ways it can be addressed in the future are described in a separate [rationale](constsize.md). + +The expression comes in different forms. In the first form, the `s1`, `s2`, …, `sn` are constant variability `Integer` expressions: +``` +constsize(arrExp, s1, s2, ..., sn) +``` +Here, `arrExp` has array type with at least _n_ dimensions. + +In the second form, a constant variability array of `Integer` sizes is given instead: +``` +constsize(arrExp, {s1, s2, ..., sn}) +``` + +It is a type error if a constant array size of `arrExp` does not match the corresponding size of the `constsize` expression. A flexible size of `arrExp` is asserted to be equal to the corresponding size of the `constsize` expression, and any error is typically not detected until runtime. + +The `constsize` expression has the same type as `arrExp`, except that the flexible array dimensions are replaced by the specified constant sizes. Note that this definition also applies to array dimensions with non-`Integer` upper bounds. For example, any `Boolean` array dimension in `arrExp` must be matched by 2 in the `constsize` expression. + +Example: +``` +model M + function f + output Real[2, :] y; + ... + end f; + function g + output Real[2, :, 4] y; + ... + end g; + function h + output Real[Boolean, :] y; + ... + end h; + Real[2, 3] a = constsize(f(), 2, 3); /* OK. */ + Real[2, 3] b = constsize(f(), size(b)); /* OK. */ + Real[2, 3, 4] c = constsize(g(), 2, 3); /* OK. */ + Real[Boolean, 3] b = constsize(f(), 2, 3); /* OK; a Boolean dimension has size 2. */ +end M; +``` + +### Variability of size-expressions +The variability of an `ndims` expression is constant, as it only depends on the type of the argument and is unaffected by flexible array sizes. + +The variability of a `size` expression depends on the presence of flexible array sizes in the argument. If the result depends on a flexible array size, the variability of the array argument is preserved, otherwise, the variability of the `size` expression is constant. + +#### Change and reason for the change +In Modelica, size-expressions are described as function calls, meaning that they cannot be seen as acting on the type of the argument. This is changed in order to capture important cases of specifying array dimensions that would otherwise be typed as flexible sizes for no good reason. + +### Component declaration with non-constant array size +In Flat Modelica, component declarations outside functions may only specify constant array sizes. + +#### Change and reason for the change +In Modelica, array sizes with parameter variability outside of functions are somehow allowed, at least not forbidden, but the semantics are not defined. +So it is easier to forbid this feature for now. If introduced in Modelica, it is still possible to introduce them here with the same semantics. It would be impossible the other way around. + + +## Subscripting of general expressions + +In Flat Modelica it is possible to have a subscript on any (parenthesized) expression. +The reason for this generalization is that some manipulations, in particular inlining of function calls, can lead to such +expressions and without the slight generalization we could not generate flat Modelica for them. It does not add any real complication +to the translator. + +The reason it is restricted to parenthesized expressions is that `a.x[1]` (according to normal Modelica semantics) and `(a.x)[1]` will often work differently. +Consider +``` +record R + Real x[2]; +end R; +R a[3]; +``` +Here `a.x[1]` is a slice operation in Modelica generating the array `{a[1].x[1],a[2].x[1],a[3].x[1]}`, whereas `(a.x)[1]` +is a subscripted slice operation generating the array `{a[1].x[1],a[1].x[2]}` +(assuming trailing subscripts can be skipped, otherwise it is illegal). +It would be possible to extend subscripting to `{a,b}[1]`, `[a,b][1,1]`, +and `foo()[1]` without causing any similar ambiguity - but it was not deemed necessary at the moment. + + +## Input output + +The input and output causality shall only be present at the top of the model (and in functions). + +For converting a Modelica model it means that input or output shall only be preserved +for variables that are: +* public top-level connector variables +* declared inside top-level connector variables +* public top-level non-connector scalar +* public top-level non-connector record + +Consider: +``` +connector C + input Real x; + output Real y; +end C; +record R + Real x; +end R; +connector RealInput=input Real; +model MSub + input R r; + RealInput a; + C c; + output Real z; +protected + RealInput a2; + C c2; + output Real z2; +end MSub; +model M + extends MSub; + MSub msub(r=r); +end M; +``` +The Flat Modelica for `M` should only preserve input for `r`, `a`, `c.x` and output for `c.y`, `z`, +and thus not preserve it for protected variables and for variables in `msub`. + + +## Simplify modifications + +Flat Modelica has different rules for modifications applied to: +- Model component declarations +- Types (records and short class declarations) and functions (function component declarations) + +### Common restrictions + +Some restrictions compared to full Modelica apply to both modifications in types and in model component declarations: +- Flat Modelica does not allow hierarchical names in modifiers, meaning that all modifiers must use the nested form with just a single identifier at each level. +- At each level, all identifiers must be unique, so that conflicting modifications are trivially detected. + +### Restrictions for model component declarations + +A _model component declaration_ is a component declaration belonging to the single `model` of a Flat Modelica source. + +Aside from the common restrictions, there are no other restrictions on the modifications in model component declarations. + +### Restrictions for types and functions + +Named types can be introduced in two different ways in flat modelica, where both make use of modifications: +- When defining `record` types, each _record component declaration_ can have modifications. For example: +``` +record 'PosPoint' + 'Length' 'x'(min = 0); + 'Length' 'y'(min = 0); +end 'PosPoint'; +``` +- When defining type aliases (also known as _short class declarations_). For example: + - ```type 'Length' = Real(unit = "m");``` (just modify existing scalar type) + - ```type 'Cube' = 'Length'[3](min = 0, max = 1);``` (make array type) + - ```type 'Square' = 'PosPoint'('x'(max = 1), 'y'(max = 1))``` (nested modification) + +The third and last category of component declarations (beside model component declarations and record component declarations), _function component declarations_, has the same restrictions as record component declarations, see below. This includes both public and local function component declarations. For example: +``` +function 'fun' + input Real 'u'(min = 0); /* Public function component declaration. */ + output Real 'y'(min = 0); /* Public function component declaration. */ + Real 'x'(min = 0); /* Local function component declaration. */ + … +end 'fun'; +``` + +The following restrictions apply to modifications in types and functions, making types and function signatures in Flat Modelica easier to represent and reason about compared to full Modelica: +- Attribute modifiers must have constant variability. +- Value modifiers in types can only have constant variability due to Flat Modelica scoping rules. +- Value modifiers in functions can make use of non-constant components in the same function definition, but with simplified semantics compared to full Modelica. +- Attribute modifiers must be scalar, giving all elements of an array the same element type. Details of how the scalar modifier is applied to all elements of an array is described [below](#Single-array-element-type). For example, an array in a type cannot have individual element types with different `unit` attributes. + +The modifications that are not allowed in types must be applied to the model component declarations instead. For attributes such as `start`, `fixed` and `stateSelect`, this will often be the case. + +The reason for placing the same restrictions on local function component declarations as on public function component declarations is that the handling of types inside functions gets significantly simplified without much loss of generality. To see the kind of loss of generlity, one needs to consider that many attributes have no use in functions at all: `start`, `fixed`, `nominal`, `unbounded`, `stateSelect`, and `displayUnit`. This leaves two groups of attributes with minor loss of generality: +- The strings `unit` and `quantity` can be used to enable more static checking of units and quantities in the function body. Since such checks are performed during static analysis, the constant variability requirement should hold in general, not just inside functions. Regarding the other requirement, it is hard to come up with realistic examples where `unit` and `quantity` would not be equal for all elements of an array. +- Outside functions, `min` and `max` both provide information that may be useful for symbolic manipulations and define conditions that shall be monitored at runtime. While the symbolic manipulations benefit greatly from constant variability of the limits, the runtime checking is more easily applicable to other variabilities, and different limits for different array elements is not as inconceivable as having different units. Inside functions, on the other hand, limits on local variables is not going to provide important information for symbolic manipulations, since function body evaluation does not involve equation solving. If one would like to have non-constant limits, or limits that are different for different elements of an array, this is possible to express using `assert` statements instead of `min` and `max` attributes. + +#### Single array element type + +As stated above, an array in a type must have the same type for all its elements, which is to be expressed somehow using only scalar modifiers. Exactly how this shall be enforced is left to depend on a clarification regarding the use of `each` in full Modelica, see https://github.com/modelica/ModelicaSpecification/issues/2630#issuecomment-669868185 and related comments. + +The two variants in `'LineA'` and `'LineB'` below are considered, with the aim of expressing the same thing that would be expressed as `FullModelicaLine` in full Modelica: +``` +type 'P' = Real[3]; + +record FullModelicaLine + /* Basic way of setting all 'start' attributes is to provide an array with all values: + */ + 'P' q[2](start = fill(4, 2, 3), fixed = fill(false, 2, 3)); + + /* Alternatively, one can (no full Modelica controversy here) use 'each' to + * propagate the same modifier to all elements of the surrounding array: + */ + 'P' p[2](each start = fill(4, 3), each fixed = fill(false, 3)); +end FullModelicaLine; + +record 'LineA' + /* Unclear whether or not valid Modelica. */ + 'P' 'p'[2](each start = 4, each fixed = false); +end 'LineA'; + +record 'LineB' + /* Do not use 'each' at all in Flat Modelica types. */ + 'P' 'p'[2](start = 4, fixed = false); +end 'LineB'; +``` + +If the `LineA` variant ends up being valid in full Modelica, then this is the form that will also be used for Flat Modelica. Otherwise, Flat Modelica will use the `LineB` form. + +### Final modification + +The concept of being final in full Modelica implies two different things: +- Further modification is not possible. This can be verified in the reduction from full Modelica to Flat Modelica, and there is no real need to express this constraint also in the Flat Modelica model. (It could be useful for expressing constraints for hand-written Flat Modelica, but it is a language feature we could add later if requested.) +- Parameter values and `start` attributes cannot be modified after translation. This is something that can't just be verified during the reduction from full Modelica to Flat Modelica. Instead, final parameter declaration equations are turned into initial equations, and then the same technique is used to handle final modification of `start`. Details of this are given the sections below on initialization of parameters and time-varying variables. + + +## Initialization of parameters + +In Flat Modelica, a parameter's declaration equation shall be solved with respect to the parameter, allowing the right hand side to be overridden during initialization (that is, after translation). This is similar to a full Modelica non-final parameter with `fixed = true`. + +For example, the full Modelica +``` +parameter Real p(fixed = true) = 4.2; +``` +translates to the Flat Modelica +``` +parameter Real 'p' = 4.2; /* Presence of declaration equation corresponds to full Modelica fixed = true. */ +``` + +In Flat Modelica, a parameter without declaration equation shall be solvable from equations given in the `initial equation` section. This corresponds directly to the full Modelica parameters with `fixed = false`. + +For example, the full Modelica +``` + parameter Real p(fixed = false); +initial equation + p^2 + p = 1; +``` +translates to the Flat Modelica +``` + parameter Real 'p'; /* Full Modelica parameter with fixed = false. */ +initial equation + 'p'^2 + 'p' = 1; +``` + +The same mechanism is also able to represent a full Modelica final declaration equation. + +For example, the full Modelica +``` + final parameter Real p = 4.2; +``` +translates to the Flat Modelica +``` + parameter Real p; /* Full Modelica final parameter has no declaration equation in Flat Modelica. */ +initial equation + p = 4.2; /* From full Modelica final declaration equation. */ +``` + +The handling of guess values needed to solve parameters from nonlinear equations is the same as for time-varying variables, and is described in the next section. + + +## Attributes `start`, `fixed`, and final modification of `start` + +### Background for start-values in Modelica + +The handling of start-values in Modelica is complicated by several aspects: +- The interaction between start and fixed. +- The priorities for non-fixed start-values. +- Whether start-values can be modified or not afterwards. This is related to final start-values. +This information is hidden and not easy to understand, and is not even easy to modify in Modelica. +As a simplifying assumption we could assume that only literal start-values can be modified afterwards (but not that all literal start-values can be modified). + +Flat Modelica cannot simply preserve the priorities, since they are based on where a modification occurs - and that information is gone. +Removing the complexity of priorities would require that start-values have been prioritized before generating Flat Modelica, which requires that index-reduction and state-selection is performed earlier, which is contrary to the goal. +Replacing start-values by initial equations would require that prioritization has been done, and also prevent experimenting with novel ideas for initialization; see "Investigating Steady State Initialization for Modelica models" by Olsson & Henningsson (Modelica 2021 conference). +Treating fixed and non-fixed variables differently doesn't work if we want to preserve arrays, since different array elements may have different values for `fixed`. + +To consider different start-values consider the following Modelica model: +``` +model A + model B + model M + Real x(start = 1.0); + Real y; + Real z; + equation + y = 5 * x; + z = 7 * x; + x + y + z = sin(time + x + y + z); + end M; + M m1; + M m2(z(start = 2.0)); + M m3(y(start = 3.0)); + end B; + B b(m2(y(start = 4.0))) +end A; +``` +Variable | Start-value | Priority in Modelica +--------|--------------|------ +`b.m1.x` | 1.0 | 3 +`b.m1.y` | | +`b.m1.z` | | +|| +`b.m2.x` | 1.0 | 3 +`b.m2.y` | 4.0 | 1 +`b.m2.z` | 2.0 | 2 +|| +`b.m3.x` | 1.0 | 3 +`b.m3.y` | 3.0 | 2 +`b.m3.z` | | + +In this case it is recommended to use `b.m1.x`, `b.m2.y`, and `b.m3.y` as iteration variables in the non-linear equations. + +For initialization these start-values can also be used for selecting additional start-values while also considering fixed-attributes. + +#### Heterongenous arrays with fixed +The `fixed` attribute can vary between array elements in Modelica. + +A non-contrived example is: +``` +block SimpleFilter + parameter Real k = 2; + Real x[3](each start = 0.0, fixed = {false, true, true}); + output Real y(start = 1.0, fixed = true) = k * x[1]; + input Real u; +equation + der(x) = cat(1, x[2 : end], {u}); +end SimpleFilter; +``` +In this case the first state is not fixed, instead the output is fixed (in some cases the output may be in another sub-model). + +#### Start-value for parameters +For parameters the start-value is normally irrelevant and not specified. +If the parameter lacks a value modification the `start` attribute can be used as parameter-value after a warning, this can be done before generating Flat Modelica (if `fixed = true`). + +The real problem is if the parameter has `fixed = false` and no value (but possibly a start-value). + +As an example: +``` +model SteadyStateInit + parameter Real p(start = 2, fixed = false); + Real x(start = 10, fixed = true); +initial equation + der(x) = 0; +equation + der(x) = 10 - p * x; +end SteadyStateInit; +``` +In more a complicated situation, this could be the length of a mechnical arm that must be adjusted based on initial configuration. + +### Implicitly declared guess value parameter + +Instead of controlling the guess values for the variable `x` via its `start` attribute as in full Modelica, Flat Modelica makes use of an implicitly declared parameter `guess('x')`. This is called the _guess value parameter_ for `'x'`, and has the same type as `x`. + +The syntax makes use of the new keyword `guess` which is not present in full Modelica. (Note that introducing a new keyword will not cause conflict with identifiers used in full Modelica code thanks to name mangling.) + +Since the declaration of `guess('x')` is implicit, a declaration equation cannot be provided in the same was as for a declared parameter. Instead, a special form of _parameter equation_ is used, where the parameter being solved must appear on the left hand side, and the equation shall be solved with causality so that the right hand side can be overridden during initialization (that is, after translation). In the grammar, it is an new alternative in _generic-element_: + +> _generic-element_ → ~~_import-clause_ | _extends-clause_ |~~ _normal-element_ | _parameter-equation_ + +> _parameter-equation_ → **parameter** **equation** _guess-value_ **=** _expression_ _comment_ + +> _guess-value_ → **guess** `[(]` _component-reference_ `[)]` + +(One can consider more general use of parameter equations in the future, but for now they are only used for guess value parameters.) + +For example: +``` +Real 'x'; +parameter equation guess('x') = 1.5; +Real 'y'; /* Parameter equation above does not leave public section. */ +``` + +Similar to `pre('x')`, `guess('x')` acts as an independent variable in the initialization problem, and instead of providing a parameter equation, it can be determiend by an initial equation. Since an initial equation cannot be modified after translation, this form is useful when the full Modelica modification of `start` was final. + +For example: +``` + Real 'x'; +initial equation + guess('x') - 1.5 = 0; +``` + +When a guess value for `'x'` is needed to solve an equation (or system of equations), this equation has an implicit dependency on `guess('x')`, and it is an error if `guess('x')` cannot be determined first. For example, this is illegal: +``` +model 'IllegalGuessDependency' + Real 'x'; +initial equation + guess('x') = 0.5 * 'x'; /* Cannot determine guess('x') before solving for 'x'. */ +equation + 'x' * 'x' = time * time; /* Initialization depends on guess('x'). */ +model 'IllegalGuessDependency' +``` + +While a guess value parameter is allowed to appear in equations in the same ways as a normal parameter, Flat Modelica models originating from full Modelica are only expected to have `guess('x')` appearing in an initial equation in solved form, if appearing at all: +``` + Real 'x'; +initial equation + guess('x') = 1.5; +``` + +Default parameter equations for guess value parameters shall be added as needed to obtain a balanced initialization problem. For example: +``` + Real 'x'; +initial equation + 'x'^2 + 'x' = 1; /* Needs guess value for 'x' */ +``` +should be conceptually extended to: +``` + Real 'x'; + parameter equation guess('x') = 0.0; /* Default guess value. */ +initial equation + 'x'^2 + 'x' = 1; /* Needs guess value for 'x' */ +``` + +The need for a default equation can also come from direct use of `guess('x')`: +``` + Real 'x'; +initial equation + 'x' = guess('x'); +``` + +With the use of guess value parameters, the `SteadyStateInit` full Modelica example above can be turned into Flat Modelica: +``` +model 'SteadyStateInit' + parameter Real 'p'; + parameter equation guess('p') = 2; + Real 'x'; + parameter equation guess('x') = 10; +initial equation + der('x') = 0; + 'x' = guess('x'); +equation + der('x') = 10 - 'p' * 'x'; +end 'SteadyStateInit'; +``` + +#### Arrays and records + +For arrays, full Modelica modification of `start` with `each` will be described below. Here is a simple example without `each`: +``` + Real[3] 'x'; + parameter equation guess('x') = fill(1.5, 3); + Real[3] 'y'; + Real[2] 'z'; +initial equation + guess('y') = fill(1.5, 3); + guess('z'[1]) = 1.5; + guess('z'[2]) = 1.5; +``` + +Records are similar to arrays: +``` + record 'R' + Real 'a'; + Real 'b'; + end 'R'; + 'R' 'x'; + parameter equation guess('x') = R(1.1, 1.2); + 'R' 'y'; + 'R' 'z'; +initial equation + guess('y') = R(1.1, 1.2); + guess('z'.'a') = 1.1; + guess('z'.'b') = 1.2; +``` + +Combining arrays and records: +``` + record 'R' + Real[3] 'a'; + Real 'b'; + end 'R'; + 'R'[2] 'x'; + parameter equation guess('x') = fill(R(fill(1.5, 3), 1.2), ,2); + 'R'[2] 'y'; + 'R'[2] 'z'; +initial equation + /* Some of the many ways to give equations for all guess values: */ + guess('y') = fill(R(fill(1.5, 3), 1.2), 2); + guess('z'.'a') = fill(1.5, 2, 3); + guess('z'[1].'b') = 1.2; + guess('z'[2].'b') = 1.2; +``` + +#### Implementation notes + +Unlike normal parameters, the value of `guess('x')` is not considered part of a simulation result, allowing tools to strip all unused guess value parameters from the initialization problem. + +### Final modification of `start` + +Consider the full Modelica: +``` + Real x(final start = 1.0); +``` + +In Flat Modelica, the fact that the modification of `start` is final means that the guess value parameter shall be determined by an initial equation rather than a parameter equation: +``` + Real 'x'; +initial equation + guess('x') = 1.0; /* From final modification of start in full Modelica. */ +``` + +As another example, consider a final non-fixed parameter in full Modelica: +``` + final parameter Real p(fixed = false, start = 1.0); /* All modifications are final. */ +initial equation + p * p = 2; +``` +Flat Modelica: +``` + parameter Real 'p'; +initial equation + 'p' * 'p' = 2; + guess('p') = 1.0; /* From final modification of start in full Modelica. */ +``` + +### Modification of `start` with `each` + +When modifying arrays of `start` attributes in full Modelica, one can take advantage of `each` to avoid the need to use `fill` to create an array of suitable size with equal elements. As shown in the examples above, using `fill` can actually work as a replacement for `each` when it comes to setting guess value parameters, and the design proposed in this section may not add enough value compared to added complexity of the design. + +Consider the following full Modelica model: +``` + record R + Real[3] a; + Real b; + end R; + R[2] x(a(each start = 1.5), b(each start = 1.2)); + R[2] y(each a(start = {1.5, 1.6, 1.7}), each b(start = 1.2)); +``` + +In Flat Modelica, the parameter equation syntax can be extended to allow `each` in a similar way: +``` + parameter equation each guess('x'.'a') = 1.5; + parameter equation each guess('x'.'b') = 1.2; + parameter equation each guess('y'.'a') = {1.5, 1.6, 1.7}; + parameter equation each guess('y'.'b') = 1.2; +``` + +Just like for normal modifications, the `each` is actually redundant and could be removed from the design; the array dimensions of the right hand side must match the trailing array dimensions of the component reference on the left hand side, and the `each` is required just to make it more obvious that the right hand side value will be used to fill an array of values. (To make an actually meaninful use of `each` one needs the expressive power of selecting which array dimensions to fill, and this can be added in a backwards compatible way to future versions of Flat Modelica, by allowing `each` at different positions inside the component reference of `guess`.) + +Finally, consider a full Modelica model with final modifications of `start`: +``` + final R[2] x(a(each start = 1.5), b(each start = 1.2)); + final R[2] y(each a(start = {1.5, 1.6, 1.7}), each b(start = 1.2)); +``` +Here, the general equation syntax could be extended similar to the parameter equations: +``` +initial equation + each guess('x'.'a') = 1.5; + each guess('x'.'b') = 1.2; + each guess('y'.'a') = {1.5, 1.6, 1.7}; + each guess('y'.'b') = 1.2; +``` + +Here, the `each` is associated with the equation's entire left hand side, and for clarity at the cost of symmetry, it should only be allowed for the left hand side of an equation. As for modification with `each`, the array dimensions of the right hand side must match the trailing array dimensions of the left hand side, and the `each` corresponds to a `fill` on the right hand side, adding the missing dimensions needed to match the left hand side. + +Again: The two new uses of `each` (in parameter equations and in normal equations) add non-essential complexity to the first version of Flat Modelica, and might make more sense to add in future versions. At the same time, the proposed use of `each` in normal equations have applications beyond guess value parameters, in particular when a full Modelica model has a final modification with `each` for an array of parameter values. + +### The `fixed` attribute + +The `fixed` attribute has been completely removed in Flat Modelica. This was described above for parameters, and is described here for time-varying variables. + +When the full Modelica variable has `fixed = true`, this is represented explicitly with an initial equation in Flat Modelica. Having `fixed = false` in full Modelica doesn't turn into anything in Flat Modelica. + +For example, the full Modelica +``` + Real x(fixed = true, start = 1.0); +``` +is translated to the Flat Modelica +``` + Real 'x'; + parameter equation guess('x') = 1.0; /* From non-final modification of start in full Modelica. */ +initial equation + 'x' = guess('x'); /* From fixed = true in full Modelica. */ +``` + +Just like in Full Modelica, such equations shall also be added as needed to obtain a balanced initialization problem. For example, +``` + Real 'x'; + parameter equation guess('x') = 1.0; +``` +may be conceptually extended to: +``` + Real 'x'; + parameter equation guess('x') = 1.0; +initial equation + 'x' = guess('x'); /* Default initial equation for 'x'. */ +``` + +This can happen in combination with default parameter equations for guess values. For example, +``` + Real 'x'; +``` +may be conceptually extended to: +``` + Real 'x'; + parameter equation guess('x') = 0.0; /* Default guess value. */ +initial equation + 'x' = guess('x'); /* Default initial equation for 'x'. */ +``` + +Note that omitting the guess value parameter would not give the same result: +``` + Real 'x'; +initial equation + 'x' = 0.0; /* Wrong: No way to override after translation. */ +``` + +#### Arrays with `each` modification of `fixed` + +Nothing special is needed when a full Modelica array has a homogeneous modificaiton of `fixed` using `each`. The modification `each fixed = false` doesn't turn into anything in Flat Modelica, while `each fixed = true` turns into an array equation. + +For example, the full Modelica +``` + Real[3] x(each fixed = true, start = {1.1, 1.2, 1.3}); +``` +is translated to the Flat Modelica +``` + Real[3] 'x'; + parameter equation guess('x') = {1.1, 1.2, 1.3}; /* From non-final modification of start in full Modelica. */ +initial equation + 'x' = guess('x'); /* Array equation from each fixed = true in full Modelica. */ +``` + +#### Arrays with heterogeneous modification of `fixed` + +Nothing special is needed to handle arrays with heterogeneous modification of `fixed`. For example, the full Modelica +``` +Real[3] x(each start = 1.0, fixed = {true, false, true}); +``` +is translated to the Flat Modelica +``` + Real[3] 'x'; + parameter equation guess('x') = fill(1.0, 3); +initial equation + 'x'[1] = guess('x'[1]); + 'x'[3] = guess('x')[3]; /* One can also apply guess to the entire 'x'. */ +``` + +### Guess value prioritization + +In full Modelica, there is a priority associated with the effective modifier for a variable's `start` attribute. Details of how the priority is determined are outside the scope of Flat Modelica specification, but Flat Modelica needs a way to directly express a variable's guess value priority as a number (computed based on the full Modelica definition of priority). + +The basic Flat Modelica way of expressing a variable's guess value priority take the form of a special kind of initial equation: +``` +initial equation + prioritize('x', 2); /* The guess value priority of 'x' is 2. */ +``` + +The second argument of `prioritize` – denoted _priority_ in the grammar – shall be an `Integer` constant. Lower value means higher priority; that is, when making a choice based on priority, the variable with lower _priority_ value should be given precedence. + +Specification of priority is only allowed for components whose guess value parameter is explicitly present in the model. Example: +``` + Real 'x'; + parameter equation guess('x') = 1.1; + Real 'y'; + Real 'z'; +initial equation + guess('y') = 1.2; + prioritize('x', 1); /* OK: Mentioned in guess value parameter equation. */ + prioritize('y', 2); /* OK: Mentioned in initial equation. */ + prioritize('z', 3); /* Error: Guess value is not explicitly mentioned anywhere. */ +``` + +Array variables are no exception: +``` + Real[3] 'x'; + parameter equation guess('x') = {1.1, 1.2, 1.3}; +initial equation + prioritize('x', 2); +``` + +Multiple specification is an error. For example: +``` + Real 'x'; + parameter equation guess('x') = 1.1; +initial equation + prioritize('x', 100); + prioritize('x', 100); /* Not allowed, even though it is consistent with earlier specification. */ +``` + +Since records themselves don't have `start` in full Modelica, the guess value parameter and prioritization mechanism gets applied to the members of the record: +``` + 'R' 'r'; /* 'R' is a record type. */ + parameter equation guess('r'.'x') = 1.1; +initial equation + prioritize('r'.'x', 100); +``` + +#### Syntactic sugar: Prioritized guess value parameter equations + +A syntactic sugar is provided for guess value parameter equations: +``` + parameter equation guess('x') = prioritize(0.5, 2); +``` +This is defined to mean the same as: +``` + parameter equation guess('x') = 0.5; +initial equation + prioritize('x', 2) +``` +That is, in the syntactic sugar form, `prioritize` is used with different arguments compared to its basic form in an initial equation. In the syntactic sugar form, `prioritize` is wrapped around the right hand side of the parameter equation, and the variable to which the priority belongs is given by the left hand side, extracted from the `guess` wrapper. + +### The `nominal` attribute + +TODO: If we proceed with the design where `start` is no longer a type attribute, we should probably deal with `nominal` similarly, so that we get rid of all non-constant type attributes (`nominal` currently has parameter variability, but there are also applications where a time-varying `nominal` would be useful). + +### Syntactic sugars + +For convenience and recognition among full Modelica users, a model component declaration may include modifications of `fixed` and `start` as syntactic sugar. Note that this does not make `fixed` and `start` actual attributes in Flat Modelica; the syntactic sugar is only piggy-backing on the syntax for modification of attributes. + +Setting `fixed = true` on the continuous-time variable `'x'` is syntactic sugar for having: +``` +initial equation + 'x' = guess('x'); +``` + +For a discrete-time variable, `fixed = true` is syntactic sugar for having: +``` +initial equation + pre('x') = guess('x'); +``` + + +For `start`, +``` +Real 'x'(start = startExpr); +``` +is syntactic sugar for +``` +Real 'x'; +parameter equation guess('x') = startExpr; /* Non-final modification of start in full Modelica. */ +``` + +Note that it is not possible to use the syntactic sugar for a final modification of `start` in full Modelica, as this shall not be turned into a parameter equation for the guess value. + + +## Protected + +Flat Modelica does not distinguish between protected and public sections of a class definition. +For functions, this means that all component declarations with prefix `input` or `output` are considered part of the functions public interface, while all other component declarations are considered local to the implementation of the function. +Accordingly, a function component declaration which is neither input nor output is called a _local component declaration_ or _local variable_ in Flat Modelica. + +The new annotation `protected = true` provides a standardized way to indicate that a component declaration in Flat Modelica comes from a protected section in the full Modelica model. +See [`protected` annotation](annotations.md#protected). diff --git a/RationaleMCP/0031/functions.md b/RationaleMCP/0031/functions.md new file mode 100644 index 000000000..db4c06b6c --- /dev/null +++ b/RationaleMCP/0031/functions.md @@ -0,0 +1,81 @@ +# Built-in functions and operators + +## Flat Modelica and Modelica the same + +* abs +* sign +* sqrt +* div +* mod +* rem +* ceil +* floor +* integer +* sin +* cos +* tan +* asin +* acos +* atan +* atan2 +* sinh +* cosh +* tanh +* exp +* log +* log10 +* initial +* terminal +* semiLinear +* identity +* diagonal +* zeros +* ones +* fill +* linspace +* delay +* spatialDistribution +* homotopy +* sample +* scalar +* vector +* matrix +* cat +* String (quite complicated function with many kinds of arguments; could be de-overloaded before Flat Modelica) +* promote (a built-in operator used to describe other operators) +* der +* noEvent +* smooth +* pre +* edge +* change +* reinit +* EnumType(i) (indexed constant array, etc) +* Integer(enum) (easy to implement even if it could be a Flat Modelica function) +* transpose +* outerProduct +* symmetric +* cross +* skew +* min +* max +* sum +* product +* array + +## Might get different text to describe them + +* ndims +* size(A,i) +* size(A) + +Maybe also fill, etc affected? + +## Only in Full Modelica + +* ~~cardinality~~ (handle connections before Flat Modelica) +* ~~inStream~~ (handle connections before Flat Modelica) +* ~~actualStream~~ (handle connections before Flat Modelica) +* ~~getInstanceName~~ (handled before Flat Modelica; there is no instance name anymore) + +## New in Flat Modelica diff --git a/RationaleMCP/0031/grammar.md b/RationaleMCP/0031/grammar.md new file mode 100644 index 000000000..56900d70c --- /dev/null +++ b/RationaleMCP/0031/grammar.md @@ -0,0 +1,421 @@ +# Flat Modelica grammar + +The starting point for this Flat Modelica grammar is the ANTLR grammar for Modelica as proposed by [this ModelicaSpecification PR](https://github.com/modelica/ModelicaSpecification/pull/2378). + +The intention is to develop the Flat Modelica grammar as a modification (mainly consisting of restrictions) of the full Modelica grammar, and to make the differences clearly visible in this document. Hence, rather than just erasing the parts of the Modelica grammar that shouldn't be brought to Flat Modelica, these parts will be marked with a strikeout. + +The start rule of the Flat Modelica grammar below is [_flat-modelica_](#Start-rule). + + +## B1 Lexical conventions + +Each grammar rule is written as a block quote. Regular expressions for tokens are written as `inline code`, literal tokens are written in **upright boldface** (this is useful for avoiding the use of regular expressions when doing so would require protection of active characters), production rule names are written in _italics_, while parsing constructs are written in plain text. + +Parsing constructs: +- _x_ | _y_ — alternatives; either _x_ or _y_ +- _x_ _y_ — sequencing; _x_ followed by _y_ +- _x_* — zero or more repetitions +- _x_+ — one or more repetitions +- _x_? — zero or one repetitions +- EOF — end of file +- (…) — parentheses for grouping + +Repetition posfix operators have higher precedence than sequencing, which in turn has higher precedence than alternatives. + +To avoid risk of confusion with the parentheses parsing construct ( _a_ | _b_ ), literal parentheses are written in regular expression form, `[(]` _a_ | _b_ `[)]`, rather than in upright boldface, **(** _a_ | _b_ **)**. + +There are no empty productions. Hence, where there is no risk of ambiguity, the left side of an alternative is allowed to be ommitted, meaning the same as just having the right side alternative. For example, ( | _a_ | _b_ ) is the same as ( _a_ | _b_ ). + +### Whitespace and comments + +> _WS_ → ( `[ ]` | `\t` | _NL_ )+ + +> _LINE-COMMENT_ → `//[^\r\n]*` (_NL_ | EOF) + +> _ML-COMMENT_ → `/[*]([^*]|([*][^/]))*[*]/` + +> _NL_ → `\r\n` | `\n` | `\r` + +### Lexical units except for keywords + +> _IDENT_ → _NONDIGIT_ ( _DIGIT_ | _NONDIGIT_ )* | _Q-IDENT_ + +> _NONDIGIT_ → `_` | `[a-z]` | `[A-Z]` + +> _STRING_ → `"` ( _S-CHAR_ | _S-ESCAPE_ )* `"` + +The _S-CHAR_ accepts Unicode other than " and \\: +> _S-CHAR_ → _NL_ | `[^\r\n\\"]` + +> _DIGIT_ → `[0-9]` + +> _Q-IDENT_ → `'` ( _Q-CHAR_ | _S-ESCAPE_ ) ( _Q-CHAR_ | _S-ESCAPE_ | `"` )* `'` + +> _Q-CHAR_ → _NONDIGIT_ | _DIGIT_ | `[-!#$%&()*>+,./:;<>=?>@[]{}|~ ^]` + +> _S-ESCAPE_ → `\\['"?\\abfnrtv]` + +> _UNSIGNED-INTEGER_ → _DIGIT_+ + +> _EXPONENT_ → ( `e` | `E` ) ( `[+]` | `-` )? _DIGIT_+ + +> _UNSIGNED-NUMBER_ → _DIGIT_+ ( `[.]` (_DIGIT_)* )? ( _EXPONENT_ )? + + +## Start rule +> _flat-modelica_ →\ +>   _VERSION-HEADER_\ +>   **package** _IDENT_\ +>    ( _class-definition_ **;**\ +>    | _global-constant_ **;**\ +>    )*\ +>    **model** _long-class-specifier_ **;**\ +>   **end** _IDENT_ **;** + +Here, the _VERSION-HEADER_ is a Flat Modelica variant of the not yet standardized language version header for Modelica proposed in [MCP-0015](https://github.com/modelica/ModelicaSpecification/tree/MCP/0015/RationaleMCP/0015): +> _VERSION-HEADER_ → `^\U+FEFF?//![ ]flat[ ][0-9]+[.][0-9]+[r.][0-9]+$` + +The `\U+FEFF?` at the very beginning is an optional byte order mark. + +The _IDENT_ in the _flat-modelica_ rule must be the same identifier as in the _long-class-specifier_ following **model**. + +As an example of the _flat-modelica_ rule, this is a minimal valid Flat Modelica source: +``` +//! flat 3.5.0 +package _F + model _F + end _F; +end _F; +``` + + +## B22 Class definition + +> _class-definition_ → ~~**encapsulated**?~~ _class-prefixes_ _class-specifier_ + +> _class-prefixes_ →\ +>   ~~**partial**?~~\ +>   (\ +>   | **type**\ +>   | ~~**operator**?~~ **record**\ +>   | ( ( **pure** **constant**? ) | **impure** )? ~~**operator**?~~ **function**\ +>   ~~| **class**~~\ +>   ~~| **model**~~\ +>   ~~| **block**~~\ +>   ~~| **expandable**? **connector**~~\ +>   ~~| **package**~~\ +>   ~~| **operator**~~\ +>   ) + +> _class-specifier_ → _long-class-specifier_ | _short-class-specifier_ | _der-class-specifier_ + +> _long-class-specifier_\ +>   → _IDENT_ _string-comment_ _composition_ **end** _IDENT_\ +>   ~~| **extends** _IDENT_ _class-modification_? _string-comment_ _composition_ **end** _IDENT_~~ + +> _short-class-specifier_ →\ +>   _IDENT_ **=**\ +>   ( _base-prefix_? _type-specifier_ ~~_array-subscripts_?~~ _class-modification_?\ +>   | **enumeration** `[(]` ( _enum-list_? | **:** ) `[)]`\ +>   )\ +>   _comment_ + +> _der-class-specifier_ → _IDENT_ **=** **der** `[(]` _type-specifier_ **,** _IDENT_ ( **,** _IDENT_ )* `[)]` _comment_ + +> _base-prefix_ → **input** | **output** + +> _enum-list_ → _enumeration-literal_ ( **,** _enumeration-literal_ )* + +> _enumeration-literal_ → _IDENT_ _comment_ + +> _composition_ →\ +>   (_generic-element_ **;**)* \ +>   ~~( **public** (_generic-element_ **;**)*~~ \ +>   ~~| **protected** (_generic-element_ **;**)*~~ \ +>   | **equation** ( _equation_ **;** )* \ +>   | **initial** **equation** ( _initial-equation_ **;** )* \ +>   | **initial**? **algorithm** ( _statement_ **;** )* \ +>   )* \ +>   ( **external** _language-specification_?\ +>    _external-function-call_? _annotation-comment_? **;**\ +>   )?\ +>   ( _annotation-comment_ **;** )? + +> _language-specification_ → _STRING_ + +> _external-function-call_ → ( _component-reference_ **=** )? _IDENT_ `[(]` _expression-list_? `[)]` + +> _generic-element_ → ~~_import-clause_ | _extends-clause_ |~~ _normal-element_ | _parameter-equation_ + +> _normal-element_ →\ +>   ~~**redeclare**?~~\ +>   ~~**final**?~~\ +>   ~~**inner**? **outer**?~~\ +>   ( ~~_class-definition_~~\ +>   | _component-clause_\ +>   ~~| **replaceable** ( _class-definition_ | _component-clause_ ) ( _constraining-clause_ _comment_ )?~~\ +>   ) + +> _parameter-equation_ →\ +>   **parameter** **equation** _guess-value_ **=**\ +>   ( _expression_ | _prioritize-expression_ )\ +>   _comment_ + +> _guess-value_ → **guess** `[(]` _component-reference_ `[)]` + +> ~~_import-clause_ →\ +>   **import**\ +>   ( _IDENT_ **=** _name_\ +>   | _name_ ( `[.]` ( `[*]` | **{** _import-list_ **}** ) | `[.][*]` )?\ +>   )\ +>   _comment_~~ + +> ~~_import-list_ → _IDENT_ ( **,** _IDENT_ )*~~ + + +## B23 Extends + +> ~~_extends-clause_ → **extends** _type-specifier_ _class-modification_? _annotation-comment_?~~ + +> ~~_constraining-clause_ → **constrainedby** _type-specifier_ _class-modification_?~~ + + +## B24 Component clause +> _component-clause_ → _type-prefix_ _type-specifier_ ~~_array-subscripts_?~~ _component-list_ + +> _global-constant_ → **constant** _type-specifier_ _array-subscripts_? _declaration_ _comment_ + +> _type-prefix_ →\ +>   ( **flow** | **stream** )?\ +>   ( **discrete** | **parameter** | **constant** )?\ +>   ( **input** | **output** )? + +> _component-list_ → _component-declaration_ ( **,** _component-declaration_ )* + +> _component-declaration_ → _declaration_ ~~_condition-attribute_?~~ _comment_ + +> ~~_condition-attribute_ → **if** _expression_~~ + +> _declaration_ → _IDENT_ _array-subscripts_? _modification_? + + +## B25 Modification + +> _modification_\ +>   → _class-modification_ ( **=** _expression_ )?\ +>   | **=** _expression_\ +>   | **:=** _expression_ + +> _class-modification_ → `[(]` _argument-list_? `[)]` + +> _argument-list_ → _argument_ ( **,** _argument_ )* + +> _argument_\ +>   → _element-modification-or-replaceable_\ +>   ~~| _element-redeclaration_~~ + +> _element-modification-or-replaceable_ →\ +>   ~~**each**?~~\ +>   ~~**final**?~~\ +>   ( _element-modification_\ +>   ~~| _element-replaceable_~~\ +>   ) + +> _element-modification_ → _name_ _modification_? _string-comment_ + +> ~~_element-redeclaration_ →\ +>   **redeclare** **each**? ~~**final**?~~\ +>   ( _short-class-definition_\ +>   | _component-clause1_\ +>   | _element-replaceable_\ +>   )~~ + +> ~~_element-replaceable_ →\ +>   **replaceable**\ +>   ( _short-class-definition_\ +>   | _component-clause1_\ +>   )\ +>   _constraining-clause_?~~ + +> ~~_component-clause1_ → _type-prefix_ _type-specifier_ _component-declaration1_~~ + +> ~~_component-declaration1_ → _declaration_ _comment_~~ + +> ~~_short-class-definition_ → _class-prefixes_ _short-class-specifier_~~ + +## B26 Equations + +> _equation_ →\ +>   ( _simple-expression_ ( **=** _expression_ )?\ +>   | _if-equation_\ +>   | _for-equation_\ +>   ~~| _connect-clause_~~\ +>   | _when-equation_\ +>   )\ +>   _comment_ + +> _initial-equation_ → _equation_ | _prioritize-equation_ + +> _statement_ →\ +>   ( _component-reference_ ( **:=** _expression_ | _function-call-args_ )\ +>   | `[(]` _output-expression-list_ `[)]` **:=** _component-reference_ _function-call-args_\ +>   | **break**\ +>   | **return**\ +>   | _if-statement_\ +>   | _for-statement_\ +>   | _while-statement_\ +>   | _when-statement_\ +>   )\ +>   _comment_ + +> _if-equation_ →\ +>   **if** _expression_ **then**\ +>    ( _equation_ **;** )* \ +>   ( **elseif** _expression_ **then**\ +>    ( _equation_ **;** )* \ +>   )* \ +>   ( **else**\ +>    ( _equation_ **;** )* \ +>   )?\ +>   **end** **if** + +> _if-statement_ →\ +>   **if** _expression_ **then**\ +>    ( _statement_ **;** )* \ +>   ( **elseif** _expression_ **then**\ +>    ( _statement_ **;** )* \ +>   )* \ +>   ( **else**\ +>    ( _statement_ **;** )* \ +>   )?\ +>   **end** **if** + +> _for-equation_ →\ +>   **for** _for-index_ **loop**\ +>    ( _equation_ **;** )* \ +>   **end** **for** + +> _for-statement_ →\ +>   **for** _for-index_ **loop**\ +>    ( _statement_ **;** )* \ +>   **end** **for** + +> ~~_for-indices_ → _for-index_ ( **,** _for-index_ )*~~ + +> _for-index_ → _IDENT_ **in** _expression_ + +> _while-statement_ →\ +>   **while** _expression_ **loop**\ +>    ( _statement_ **;** )* \ +>   **end** **while** + +> _when-equation_ →\ +>   **when** _expression_ **then**\ +>    ( _equation_ **;** )* \ +>   ( **elsewhen** _expression_ **then**\ +>    ( _equation_ **;** )* \ +>   )* \ +>   **end** **when** + +> _when-statement_ →\ +>   **when** _expression_ **then**\ +>    ( _statement_ **;** )* \ +>   ( **elsewhen** _expression_ **then**\ +>    ( _statement_ **;** )* \ +>   )* \ +>   **end** **when** + +> ~~_connect-clause_ → **connect** `[(]` _component-reference_ **,** _component-reference_ `[)]`~~ + +> _prioritize-equation_ → **prioritize** `[(]` _component-reference_ **,** _priority_ `[)]` + +> _prioritize-expression_ → **prioritize** `[(]` _expression_ **,** _priority_ `[)]` + +> _priority_ → _expression_ + + +## Expressions + +> _expression_ → _simple-expression_ | _if-expression_ + +> _if-expression_ →\ +>   **if** _expression_ **then** _expression_\ +>   ( **elseif** _expression_ **then** _expression_ )* \ +>   **else** _expression_ + +> _simple-expression_ → _logical-expression_ ( **:** _logical-expression_ ( **:** _logical-expression_ )? )? + +> _logical-expression_ → _logical-term_ ( **or** _logical-term_ )* + +> _logical-term_ → _logical-factor_ ( **and** _logical-factor_ )* + +> _logical-factor_ → **not**? _relation_ + +> _relation_ → _arithmetic-expression_ ( _relational-operator_ _arithmetic-expression_ )? + +> _relational-operator_ → **<** | **<=** | **>** | **>=** | **==** | **<>** + +> _arithmetic-expression_ → _add-operator_? _term_ ( _add-operator_ _term_ )* + +> _add-operator_ → **+** | **-** | **.+** | **.-** + +> _term_ → _factor_ ( _mul-operator_ _factor_ )* + +> _mul-operator_ → ** * ** | ** / ** | ** .* ** | ** ./ ** + +> _factor_ → _primary_ ( (**^** | **.^**) _primary_ )? + +>_primary_\ +>   → _UNSIGNED-NUMBER_\ +>   | _STRING_\ +>   | **false**\ +>   | **true**\ +>   | ( **der** | **initial** | **pure** ) _function-call-args_\ +>   | _component-reference_ _function-call-args_?\ +>   | `[(]` _output-expression-list_ `[)]` _array-subscripts_?\ +>   | `[[]` _expression-list_ ( **;** _expression-list_ )* `[]]`\ +>   | **{** _array-arguments_ **}**\ +>   | **end** + +> _type-specifier_ → **.**? _name_ + +> _name_ → _IDENT_ ( **.** _IDENT_ )* + +> _component-reference_ → **.**? _IDENT_ _array-subscripts_? ( **.** _IDENT_ _array-subscripts_? )* + +> _function-call-args_ → `[(]` _function-arguments_? `[)]` + +> _function-arguments_\ +>   → _expression_ ( **,** _function-arguments-non-first_ | **for** _for-index_ )?\ +>   | _function-partial-application_ ( **,** _function-arguments-non-first_ )?\ +>   | _named-arguments_ + +> _function-arguments-non-first_\ +>   → _function-argument_ ( **,** _function-arguments-non-first_ )?\ +>   | _named-arguments_ + +> _array-arguments_ → _expression_ ( ( **,** _expression_ )* | **for** _for-index_ ) + +> _named-arguments_ → _named-argument_ ( **,** _named-argument_ )* + +> _named-argument_ → _IDENT_ **=** _function-argument_ + +> _function-argument_\ +>   → _function-partial-application_\ +>   | _expression_ + +> _function-partial-application_ → **function** _type-specifier_ `[(]` _named-arguments_? `[)]` + +> _output-expression-list_ → _expression_? ( **,** _expression_? )* + +> _expression-list_ → _expression_ ( **,** _expression_ )* + +> _array-subscripts_ → **[** _subscript_ ( **,** _subscript_ )* **]** + +> _subscript_ → **:** | _expression_ + +> _comment_ → _string-comment_ _annotation-comment_? + +> _string-comment_ → ( _STRING_ ( **+** _STRING_ )* )? + +> _annotation-comment_ → **annotation** _class-modification_ diff --git a/RationaleMCP/0031/name-mapping.md b/RationaleMCP/0031/name-mapping.md new file mode 100644 index 000000000..95d0bbf70 --- /dev/null +++ b/RationaleMCP/0031/name-mapping.md @@ -0,0 +1,217 @@ +# Name mangling + +This document describes the Flat Modelica encoding of names of a flattended Modelica model. In the world of (full) Modelica and Flat Modelica, it is assumed that the context of an identifier is always known, so that it is always clear whether names have been mangled or not. + +## Terminology + +A _component reference_ of a flattened Modelica model is a hierarchically structured string such as `axis.bearingFriction.sa`, or `foo[1,2].bar`, or just `k`. For example, this is how we currently refer to variables in a simulation result. + +An _identifier_ is something that may be used as a variable name in a Modelica or Flat Modelica class, such as `axis` or `bearingFriction`. Valid identifiers must conform to the `IDENT` in Modelica's lexical structure (see below). + +To refer to a component reference in Flat Modelica, its name needs to be _encoded_ as a Flat Modelica identifier. This process is also known as _name mangling_ since the resulting identifiers will appear as more or less distorted variants of the original component references. A tool reading Flat Modelica input will then need to _decode_ identifiers in order to reconstruct the original component references. + + +## Requirements + +In order for Flat Modelica to be a subset of Modelica, the Flat Modelica identifiers must conform to the `IDENT` in Modelica's lexical structure: +``` +IDENT = NONDIGIT { DIGIT | NONDIGIT } | Q-IDENT +Q-IDENT = "’" { Q-CHAR | S-ESCAPE | """ } "’" +NONDIGIT = "_" | letters "a" ... "z" | letters "A" ... "Z" +DIGIT = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 +Q-CHAR = NONDIGIT | DIGIT | "!" | "#" | "$" | "%" | "&" | "(" | ")" | "*" | +"+" | "," | "-" | "." | "/" | ":" | ";" | "<" | ">" | "=" | "?" | "@" | "[" +| "]" | "^" | "{" | "}" | "|" | "~" | " "_ +S-ESCAPE = "\’" | "\"" | "\?" | "\\" | "\a" | "\b" | "\f" | "\n" | "\r" | "\t" +| "\v" +``` + +Additional requirements: +* Possible to distinguish between encoded component references and other identifiers +* Possible to reconstruct original component references +* Support different levels of scalarization +* Reserved namespace for generated names +* No collision with current and future Flat Modelica reserved names +* No ambiguity with array subscripting and record member reference expressions +* Allow systematic construction of names for things such as start attributes, that combine a variable name with additional specification + + +## Mangling rules + +### Component references + +In Modelica, a component reference is a very restricted form of a gneralized expression where literal array subscripting and record member referencing can be applied to any sub-expression. As such, they are identified with their abstract syntax tree representation. In particular, their textual input form is insensitive to whitespace and comments. + +In Flat Modelica, a component reference appears as an encoded string that is to be parsed the same way as a generalized Modelica expression for a Modelica component reference. However, a Flat Modelica component reference is not allowed to contain whitespace or comments. + +Examples: + +| String | Valid Flat Modelica component reference? | +|--|--| +| `foo[1,2].bar` | Yes | +| `foo[1, 2]` | No (whitespace not allowed) | +| `foo[1,,2]` | No (doesn't parse) | +| `foo(1,2)` | No (illegal kind of expression) | +| `foo[1/* first */]` | No (comments not allowed) | + + +### Upquoting and downquoting + +Using `\` as escape character, many strings, including all component references, can be _upquoted_ by the following reversible procedure: +1. Insert a "`\`" before any of the characters: { "`\`", "`'`" } +1. Wrap the result in single quotes. + +Upquoting a Modelica component reference always results in a valid (Flat) Modelica `Q-IDENT` identifier. Examples: + +| Input | Upquoted string | Remark | +|--|--|--| +| `axis.bearingFriction.sa` | `'axis.bearingFriction.sa'` | | +| `foo[1,2].bar` | `'foo[1,2].bar'` | | +| `'foo bar'` | `'\'foo bar\''` | | +| `'foo\''` | `'\'foo\\\'\''` | | +| `foo[1 /* first */]` | `'foo[1 /* first */]'` | Stripping whitespace and comments is not par of upquoting | +| `der(foo)` | `'der(foo)'` | Input is not valid component reference, but result is still valid identifier | +| `'foo\` | `'\'foo\\'` | Same as above. | + +To obtain the Flat Modelica component reference out of a Modelica component reference, +1. Strip whitespace and comments. +1. Upquote + +Examples: + +| Modelica component reference | Flat Modelica | Remark | +|--|--|--| +| `axis.bearingFriction.sa` | `'axis.bearingFriction.sa'` | | +| `foo[1 /* first */]` | `'foo[1]'` | Whitespace and comments on the Modelica side is stripped | + +A substring consisting of "`\`" followed by one more character is called an _escape sequence_. + +The reverse procedure is denoted _downquoting_: +1. Strip surrounding single quotes. +1. Remove the leading `\` in any escape sequence. + +Unlike upquoting, downquoting breaks much more easily. Examples: + +| Input | Downquoted string | Remark | +|--|--|--| +| `'axis.bearingFriction.sa'` | `axis.bearingFriction.sa` | | +| `'foo\''` | `foo'` | Result is not valid component reference. | +| `'\'foo\\'` | `'foo\` | Result is not valid component reference. | +| `'fo\o'` | `foo` | Escape sequence does not have to encode "`'`" or "`\`". | +| `foo'` | **error** | Input is not a valid identifier. | +| `foo` | **error** | Input does not have surrounding quotes. | +| `'foo\'` | **error** | Incomplete escape sequence after stripping surrounding quotes. | + + +### Flat Modelica namespaces + +To support fast categorization of identifiers in Flat Modelica, they are divided into easily recognizable categories: +* A `Q-IDENT` that cannot be decoded is an error. +* Any other `Q-IDENT` is categorized based on the first character of the decoded string: + - A `NONDIGIT` or "`'`" means a component reference + - A "`.`" is reserved for future use + - Otherwise, it is a generated structured name +* An `IDENT` is categorized based on the first character: + - A "`_`" means a generated non-structured name + - Otherwise, it is a Flat Modelica reserved name + +Examples: + +| Flat Modelica identifier | Category | +|--|--| +| `class` | Flat Modelica reserved name (happens to be a keyword) | +| `sin` | Flat Modelica reserved name (name of built-in function) | +| `foo` | Flat Modelica reserved name (reserved for future use) | +| `_R123` | Generated non-structured name (such as an automatically generated record or introduced helper variable) | +| `'axis.bearingFriction.sa'` | Component reference: `axis.bearingFriction.sa` | +| `'\'foo bar!\'.x'` | Component reference: `'foo bar!'.x` | +| `'der(x'` | Component reference (can't be parsed) | +| `'=der(x)'` | Generated structured name (might refer to derivative of `x`) | +| `'/foo.bar/start'` | Generated structured name (might refer to `start` attribute of `foo.bar`) | +| `'foo\'` | **error** (can't be decoded) | + + +## Use cases + +This section shows what the mangling would look like in the two extreme cases of amount of scalarization. + +For the demonstration, the following simple Modelica model will be used: +``` +model ManglingTest + + model M + parameter Real p = 1.0; + Real[2] arr; + Real x = sum(arr); + equation + der(arr) = {p, 1.0}; + end M; + + model MArr + M[2] mm(p = {2.0, 3.0}); + M m(p = 4.0); + end MArr; + + MArr root; + Real y = root.m.x; + +end ManglingTest; +``` + +### Scalarized Flat Modelica + +Here, every Flat Modelica component reference refers to a scalar variable. + +``` +model 'ManglingTest' + parameter Real 'root.mm[1].p' = 2.0; + Real 'root.mm[1].arr[1]'; + Real 'root.mm[1].arr[2]'; + Real 'root.mm[1].x' = 'root.mm[1].arr[1]' + 'root.mm[1].arr[2]'; + parameter Real 'root.mm[2].p' = 3.0; + Real 'root.mm[2].arr[1]'; + Real 'root.mm[2].arr[2]'; + Real 'root.mm[2].x' = 'root.mm[2].arr[1]' + 'root.mm[2].arr[2]'; + parameter Real 'root.m.p' = 4.0; + Real 'root.m.arr[1]'; + Real 'root.m.arr[2]'; + Real 'root.m.x' = 'root.m.arr[1]' + 'root.m.arr[2]'; + Real 'y' = 'root.m.x'; +equation + der('root.mm[1].arr[1]') = 'root.mm[1].p'; + der('root.mm[1].arr[2]') = 1.0; + der('root.mm[2].arr[1]') = 'root.mm[2].p'; + der('root.mm[2].arr[2]') = 1.0; + der('root.m.arr[1]') = 'root.m.p'; + der('root.m.arr[2]') = 1.0; +end 'ManglingTest'; +``` + +### Hierarchical Flat Modelica + +Here, automatically generated records are used to preserve the hierarchical structure of the original Modelica model, and arrays are not scalarized. + +``` +record _R1 + parameter Real 'p'; + Real[2] 'arr'; + Real 'x'; +end _R1; + +record _R2 + _R1[2] 'mm'('p' = {2.0, 3.0}); + _R1 'm'('p' = 4.0); +end _R2; + +model 'ManglingTest' + _R2 'root'; + Real 'y' = 'root'.'m'.'x'; +equation + 'root'.'mm'[1].'x' = sum('root'.'mm'[1].'arr'); + 'root'.'mm'[2].'x' = sum('root'.'mm'[2].'arr'); + 'root'.'m'.'x' = sum('root'.'m'.'arr'); + der('root'.'mm'[1].'arr') = {'root'.'mm'[1].'p', 1.0}; + der('root'.'mm'[2].'arr') = {'root'.'mm'[2].'p', 1.0}; + der('root'.'m'.'arr') = {'root'.'m'.'p', 1.0}; +end 'ManglingTest'; +``` diff --git a/RationaleMCP/0031/type-aliases.md b/RationaleMCP/0031/type-aliases.md new file mode 100644 index 000000000..5a1f4e829 --- /dev/null +++ b/RationaleMCP/0031/type-aliases.md @@ -0,0 +1,180 @@ +# Type aliases + +This document presents some alternative designs for the allowed use of type aliases in Flat Modelica. Once the different alternatives are properly described together with their advantages and disadvantages, it will be possible to make an informed decision of which design to choose. + + +## Design alternatives for scalars + +In #2468, we ended up discussing type aliases for scalar variables, and finally agreed to proceed with the alternative _Type aliases with default attributes_, where the following would be legal use of type aliases: + +``` +type Length = Real(quantity = "Length", unit = "m", displayUnit = "m"); +Length thickness(min = 0, displayUnit = "cm"); +type BigLength = Length(nominal = 1e6); +``` + +That is, built-in types such as `Real` have default behavior (not all of which that can currently be expressed with values) for all built-in attributes. A type alias can modify any subset of attributes. The semantics of each type alias definition is simply to modify zero or more of the attributes of an existing scalar type. + +The other design alternatives can be found in the #2468 discussions. + + +## Design alternatives for records + +Consider making variants of this type: + +``` +record Interval + Real low; + Real high; +end Interval; +``` + +One of the variants will be to use a type alias to create the following type: +``` +record PositiveInterval + Real low(min = 0.0); + Real high(min = 0.0); +end PositiveInterval; +``` + +Of course, the example is too small to illustrate the scalability benefit of type aliases when used in deeply nested structures; the point of the example is only to illustrate different principles of how a record type can be modified when constructing a type alias. + +### No type aliases for records + +Without type aliases for records there are at least two principal approaches to work around the limitation. + +Workaround 1: Use modified copy of `Interval` definition: +``` +record PositiveInterval + Real low(min = 0.0); + Real high(min = 0.0); +end PositiveInterval; + +Interval interval1; +PositiveInterval posinterval1; +PositiveInterval posinterval2; +``` + +Workaround 2: Just use `Interval` and put all modifiers on the instances: +``` +Interval interval1; +Interval posinterval1(low(min = 0.0), high(min = 0.0)); +Interval posinterval2(low(min = 0.0), high(min = 0.0)); +``` + +### Record type aliases with attribute modifications + +With attribute modifications allowed in a record type alias, one would do this: + +``` +record PositiveInterval = Interval(low(min = 0.0), high(min = 0.0)); +``` + +Syntactically, `min` could have been the name of a normal record member being bound to 0.0, but this interpretation is forbidden in this design alternative. + +One could consider restricting the depth of the modifications, but as seen here, a depth of 2 is needed to be of any use at all. It is not clear what would be gained by retricting the depth to 2 given that some degree of nesting is required anyway. + +### Record type aliases with modifications and bindings + +With bindings allowed in record type aliases, one could even do this: + +``` +record IntervalFromZero = Interval(low = 0.0, high(min = 0.0)); +``` + +If record type aliases were to be allowed in some form, the above would also make sense as long as the equivalent would be allowed in a long record definition. This is still something we haven't discussed. + + +## Design alternatives for arrays + +### No type aliases for arrays + +Avoids potential problems with array type aliases, but forces users that still want the abstraction to introduce record with dummy member: + +``` +record Point3D "Workaround for lack of array type aliases" + Length[3] member; /* More likely to be called something short, like 'x'. */ +end Point3D; + +record PositiveCone3D + Length[3] member(each min = 0.0); +end PositiveCone3D; + +record PositiveCone3DPair_A + Length[2, 3] member(each min = 0.0, each max = {10.0, 11.0, 12.0}, each unbounded = {false, true, false}); +end PositiveCone3DPair_A; + +record PositiveCone3D_Bounded "Bounded PositiveCone3D with different upper bounds" + PositiveCone3D member(member(max = {10.0, 11.0, 12.0}, unbounded = {false, true, false})); /* Note appearance of 'member' from PositiveCone3D. */ +end PositiveCone3D_Bounded; + +record PositiveCone3DPair_B + PositiveCone3D_Bounded member[2]; +end PositiveCone3DPair_B; + +record PositiveCone3DPair_C + PositiveCone3D member[2](each member(max = {10.0, 11.0, 12.0}, unbounded = {false, true, false})); /* Note appearance of 'member' from PositiveCone3D. */ +end PositiveCone3DPair_C; +``` + +### Array type aliases with implicit `each` on all modifications + +Allow array dimensions to be added in a type alias, but don't allow the alias to introduce differences between array entries by taking all modifications as having an implicit `each`: + +``` +type Point3D = Length[3]; + +type PositiveCone3D = Length[3](min = 0.0); + +type PositiveCone3DPair_A = Length[2, 3](min = 0.0, max = 10.0, unbounded = false); +``` + +It gets more complicated with nested array types: + +``` +type PositiveCone3DPair_B = PositiveCone3D[2](max = 10.0, unbounded = false); /* Allowed? */ + +type PositiveCone3DPair_C = PositiveCone3D[2](max = {10.0, 11.0, 12.0}, unbounded = {false, true, false}); /* Allowed? */ +``` + +If those are not allowed, one would be forced to use an intermediate record with dummy member as workaround: + +``` +record PositiveCone3D_Bounded "Bounded PositiveCone3D with different upper bounds" + PositiveCone3D member(max = {10.0, 11.0, 12.0}, unbounded = {false, true, false}); +end PositiveCone3D_Bounded; + +type PositiveCone3DPair_D = PositiveCone3D_Bounded[2]; +``` + +The exact syntax for specifying the array dimensions is the topic of #2468, but unfortunately some of the early discussion has been lost due to rebasing the PR branch. To summarize, the syntax used above has a non-intuitive effect when array dimensions are nested: + +``` +model ModelicaAppendDimensions + type Arr = Real[3, 4]; + Arr[1, 2] x = fill(0.0, 1, 2, 3, 4); /* Note: (Real[k, l])[m, n] is *not* the same as Real[k, l, m, n]. */ +end ModelicaAppendDimensions; +``` + +A remedy to this would be to place array dimensions before the element type: + +``` +model PrependDimensions + type Arr = [3, 4] Real; + [1, 2] Arr x = fill(0.0, 1, 2, 3, 4); /* Note: [k, l]([m, n]Real) is the same as [k, l, m, n]Real. */ +end PrependDimensions; +``` + +With the dimensions prepended, the modifications would also appear more naturally as applied to the element type: +``` +type PositiveCone3D = [3] Length(min = 0.0); +``` + +## Conclusions + +While type aliases for scalars and records were deemed useful enough to be supported, it was decided to omit type aliases for arrays. + +The reasoning beind the decision to not support array type aliases was based on several things: +- Array type aliases were not thought to add significant convenience for machine-generated Flat Modelica. +- The full Modelica way of defining array type aliases comes with some confusion when used as the element type of another array type (see above). +- The idea to introduce another notation for declaring array types (as in `[3, 4] Real`) was not popular in the MCP working group. diff --git a/RationaleMCP/0031/variability-constrained-types.md b/RationaleMCP/0031/variability-constrained-types.md new file mode 100644 index 000000000..094ed4810 --- /dev/null +++ b/RationaleMCP/0031/variability-constrained-types.md @@ -0,0 +1,84 @@ +# Variability-constrained types by example + +[As described elsewhere](differences.md), a variability-constrained type is one where some record members have been declared with variability prefix (`parameter` or `constant` – `discrete` is currently _not_ an actual variability prefix in Modelica, but implies being discrete-time in a way that makes it impossible to use in a type). In Flat Modelica, such types are only allowed in model component declarations, and this document gives examples of how this constraint be handled. + +## Hierarchical representation of model structure + +This model represents the typical situation in full Modelica, where parameters live side by side with time-varying variables at different levels of the component hierarchy: +``` +model M + model Resistor + parameter Real r; + Real i; + equation + i = 1 / r; + end Resistor; + + parameter Real r1; + Resistor comp1(r = r1); + Resistor comp2; + Real i1 = comp1.i; +end M; +``` + +The same hierarchical structure can be represented in Flat Modelica: +``` +record 'M.Resistor' /* Variability-constrained type */ + parameter Real 'r'; /* Parameter declared with variability-free type Real. */ + Real 'i'; +end 'M.Resistor'; + +model 'M' + parameter Real 'r1'; /* Parameter declared with variability-free type Real. */ + 'M.Resistor' 'comp1'('r' = 'r1'); + 'M.Resistor' 'comp2'; + Real 'i1' = 'comp1'.'i'; +equation + 'comp1'.'i' = 1 / 'comp1'.'r'; + 'comp2'.'i' = 1 / 'comp2'.'r'; +end 'M'; +``` + +For this use of a variability-constrained Flat Modelica record, there is typcailly no need to have a variability-free variant of the same type, and the interesting part is to verify that the Flat Modelica equations don't have illegal sub-expressions of variability-constrained type. Consider the first equation, +``` + 'comp1'.'i' = 1 / 'comp1'.'r'; +``` +To start with each occurrence of `'comp1'` (which has variability-constrained type) is in the form of a component reference. The first of these, `'comp1'.'i'` is a continuous-time expression of type `Real`. The other, `'comp1'.'r'` is a parameter expression of type `Real`. That is, `'comp1'.'r'` does not have variability-constrained type; it is just a normal expression having parameter variability. Hence, there are no illegal occurrences of sub-expressions of variability-constrained type. + +## Records with the same constraint on all members + +An easy example to start with is a record that is only meant to be used for parameters (such records can be found, for example, in `Modelica.Electrical.Batteries.ParameterRecords`): +``` +model M + record Parameters + parameter Real x; + parameter Real y; + end Parameters; + + parameter Parameters[2] data = {Parameters(1, 2), Parameters(3, 4)}; + Analog.Basic.Resistor[2] r(final R = data.x, final T_ref = data.y); +end M; +``` + +(Someone has to remind us why the `data` was additionally declared `parameter`.) + +In this case, there seems to be no need to preserve the variability-constraints in the Flat Modelica type: +``` +record 'M.Parameters' + Real 'x'; + Real 'y'; +end 'M.Parameters'; + +model 'M' + parameter 'M.Parameters'[2] 'data' = {'M.Parameters'(1, 2), 'M.Parameters'(3, 4)}; + 'Analog.Basic.Resistor'[2] 'r'('R' = 'data'.'x', T_ref = 'data'.'y'); +end 'M'; +``` + +## Record including its own array dimension + +TODO: Example with time-varying polynomial. + +## Connector with parameter + +TODO: Describe what happens to a full Modelica connector with a parameter inside. In particular, what is the corresponding Flat Modelica record?