Closed casella closed 2 years ago
How System Modeler handles start value prioritization is an area we'd like to improve, and until then we don't have experimental results of any value for this discussion. I'm afraid that having truly useful criteria and making these less non-normative might be needed in order for improvement in this area to get prioritized, so in this regard I'm glad to see this issue bringing attention to the situation.
One of my concerns, however, is that very detailed criteria could lead to models with de-prioritized modifications of start
all over the place, whose modifiers will rot over time and then come back into play unexpectedly one day when the highest prioritized start
is no longer competing for the same initialization. In my experience, the best models therefore have very few "conflicts" to be resolved by start value prioritization. Hence, I think we should also consider the completely different path of reducing dependency on resolution based on priorities, and instead encourage the use of the new break
modifiers to remove the potentially conflicting modifications of start
.
Encouraging the use of break
should probably not completely replace the prioritization mechanism, but the prioritization mechanism could probably be simplified (and hence more transparent to the user) if the possibility to break
modifications is taken into account.
In my experience, the best models therefore have very few "conflicts" to be resolved by start value prioritization.
Consider that models with physical connectors typically have at least four aliases for each connector variable: two for the connector variable on each side of the connection (assuming only 2 connectors involved), and another two, one on each side, for equations relating the connector variables to the internal variables of the model, which is good modelling style. In case medium models from Modelica.Media are involved (e.g. BaseProperties), you may easily get another one or two more. Not easy to reduce this number substantially.
Hence, I think we should also consider the completely different path of reducing dependency on resolution based on priorities, and instead encourage the use of the new
break
modifiers to remove the potentially conflicting modifications ofstart
.
I'm not aware of this, can you give me some pointer to the concept? Thanks!
Hence, I think we should also consider the completely different path of reducing dependency on resolution based on priorities, and instead encourage the use of the new
break
modifiers to remove the potentially conflicting modifications ofstart
.I'm not aware of this, can you give me some pointer to the concept? Thanks!
I'm thinking of this MCP where @HansOlsson has been active this year: https://github.com/modelica/ModelicaSpecification/tree/MCP/0009/RationaleMCP/0009
@henrikt-ma, I think we are talking about two different problems.
The problem that I have here is not that I want to undo certain conflicting modifications on the same variable. The problem is that in object-oriented system models there are often several different variables that turn out to be equal because of equations (in particular connection equations, but also model equations), but have different start values. Modifying some of these start values is practical, modifying others is less practical, so you leave them to some more or less generic default value, and modifying all of them is completely impractical and against the spirit of high-level object-oriented modelling.
What we need here is some reliable means to figure out which variable of the alias set has the most meaningful start value (i.e., the one that the library designer would like to see picked by some magic), and use it as an initial guess for iterative solvers, while avoiding to pick generic default values instead. For example, System6
above has the following set of 10 variables with different start attributes:
Real ts.port.T(unit = "K", start = 300.0);
Real ts.ts.T1(unit = "K", start = 390.0);
Real ts.ts.T2(unit = "K", start = 310.0);
Real ts.ts.T3(unit = "K", start = ts.ts.Tstart);
Real ts.ts.port.T(unit = "K", start = 300.0);
Real ps.port.T(unit = "K", start = 300.0);
Real ps.ps.port.T(unit = "K", start = 300.0);
Real ps.ps.T1(unit = "K", start = 300.0);
Real ps.ps.T2(unit = "K", start = 330.0);
Real ps.ps.T3(unit = "K", start = ps.ps.Tstart);
but those 10 variables are all equal due to this set of 9 equations
ts.ts.port.T = ts.port.T;
ps.ps.port.T = ps.port.T;
ts.port.T = ps.port.T;
ts.ts.port.T = ts.ts.T1;
ts.ts.T1 = ts.ts.T2;
ts.ts.T2 = ts.ts.T3;
ps.ps.port.T = ps.ps.T1;
ps.ps.T1 = ps.ps.T2;
ps.ps.T2 = ps.ps.T3;
So, most tools will just get rid of 9 variables and replace them with the "chosen one" in the rest of the system, while removing these 9 trivial equations from the system. The question is, what is the start attribute the chosen one should have, to ensure that its use as initial guess for an iterative solver leads to successful solver convergence?
The modeller's intention in this case is very clear: 390
. If the compiler picks another one, say 300
or 330
, this may cause the failure of an iterative solver and hence a simulation failure. Basically, your model doesn't work despite the fact that you indeed set the correct value in the appropriate place. This can be very annoying. Unfortunately, it still happens all the time in practice.
What rules should we introduce, so that the compiler can figure that out automatically?
I add @hubertus65 and @jespermattsson to the discussion, as I guess they may be interested.
In order to get what I'd like, I wrote some loosely formulated requirements. They need to be formalized into actual rules for the compiler, and possibly made more compact.
- type defaults have the lowest priority, they should only be used if there are no other start value specification overriding them
- start values set by modifications when instantiating components should have higher priority than start values set by default binding equations in component declarations
- the priority of start values given by parameters should be determined on the basis of where the actual parameter value is specified
Yes, the latter is quite important to get good results and we have added that in Dymola (to break ties I think) - but we had a different priority for literal parameter values causing the issue for the remaining cases.
@henrikt-ma, I think we are talking about two different problems.
No, what you elaborate is also what I have in mind. However, your example shows exactly what I mean by sub-standard modeling style, because one should avoid being dependent on elaborate prioritization schemes for understanding what will be the active start
attribute in the end.
I see that having start
defined in a type can be useful, and agree that this should have the lowest priority. The problematic part is when modifications are made all over the place when instantiating components, so that one has to prioritize among these. By removing all such modifications but one, it gets much more clear what to expect, and if the variable with the modification for some reason is removed from the alias set, the tool could even notify the user about picking a start
from a type so that the user gets aware of the need to add a replacement start
"instantiation modification". If the tool does this intelligently (keeping quiet unless the start
actually matters) and notifying when there was a need to resolve based on the finer priority level, I think this would give a great user experience, and the model would be much easier to understand where to modify in order to affect initialization.
Yes, the latter is quite important to get good results and we have added that in Dymola (to break ties I think)
This probably explains the higher success rate of Dymola in my small test suite. I wonder if you should rather use it all the time, rather than just to break ties.
but we had a different priority for literal parameter values causing the issue for the remaining cases.
Aha. This can also be a problem when parameter are evaluated, because the information where the value comes from may get lost.
No, what you elaborate is also what I have in mind. However, your example shows exactly what I mean by sub-standard modeling style, because one should avoid being dependent on elaborate prioritization schemes for understanding what will be the active
start
attribute in the end.
It may be sub-standard in your opinion, but it nevertheless has a rationale 😄
I guess the story was never explicitly told, so let me try to elaborate on that a bit.
Setting start values to get successful solver convergence is a real pain in the a##. It is the one thing that breaks the magic of EOO modelling. We say that Modelica allows to write equations as in textbooks (true), allows full modularity when composing models (true), allows engineers to re-use advanced modelling knowledge embedded in reusable component libraries (true), enables model-solver separation (true), and allows working at a much higher level of abstraction (also true).
However, when the solver fails, the veil is torn and you have to stare into the abyss and try to figure out how to fix the mess. It's an ugly experience, and oftentimes requires guru-level understanding, hampering a wider use of Modelica in many fields of application. Not much in mechanics (which explains the wide success of Modelica in the automotive industry), a lot more in thermo-fluid and energy systems, which IMHO explains why it is not as widely used there as it could.
So, what library developers (including myself) do is trying as much as possible to infuse their expert knowledge in the code, to avoid as much as possible that end users go through this painful experience, so that the promises of EOO modelling are actually fulfilled.
The only viable strategy I see here is defence in depth.
The first layer is to set some start values with broad applicability in type definitions, which are already good because they may, e.g., avoid divisions by zero that you would get with the default start value of the Real type. In some cases, this is enough for robust solvers to achieve convergence. In many cases, it is not enough.
The next layer of defense is to provide some component-specific start values, typically through parameters as Tstart or pstart, that have more specific defaults than the basic types, and can be further refined upon instantiation by the end user, through a parameter window in the GUI. These defaults must have a higher priority than type-defined start attributes, and an even higher priority should be given if the default in the library model is overridden by a modifer applied by the end user in his own model.
Composite device models may have their own start value parameters, which are then passed (possibly with some massaging) to their consitutive components. Their priority is even higher.
In some cases this is not enough to avoid the ripping of the veil. At that point, deus-ex-machina solutions can be employed: if I see that a certain initial guess is problematic, I can poke it from the top to some appropriate literal value, via nested modifiers. Of course a start value defined in this way should have top priority and override whatever heuristics is built-in the library.
It goes without saying that an end-user shouldn't have to worry about de-activating higher- or lower-priority attributes. In most cases he or she wouldn't have a clue. The whole point of using Modelica tools is to have all (or as many as possible) low-level issues related to the solution process to be handled automatically by the tool.
So, start attribute modifications are all over the place for a good reason. And yes, we need Modelica tools to sort them out automatically, based on some standardized priority rules that a library developer can depend upon 😄
I add @rfranke and @kabdelhak to the loop, as they might be interested
Reading the cited paragraph of the specification, the expected start values appear reasonable. Can it be that OMC gives too much priority to start values specified with parameters?
No idea.
I guess we should implement the new specification, once the updated criterion is merged in. We can also do it earlier - one hand there is a risk that the proposal is then changed after more discussion, on the other hand we may give a good contribution to it, based on actual experience
The selection of the most appropriate start attribute for alias variables that are eventually involved in nonlinear systems of equations is a critical feature of Modelica compilers, because it can determine the success or failure of initialization in models with nonlinear initial equations systems.
The topic is addressed in Section 8.6.2 of the specification.
Unfortunately, the currently stated criteria seem to me not to be completely up to the task. I am constantly getting truckloads of warnings about inconsistent start values in situations where in my opinion there should be no conflict at all. I believe the whole subject should be reviewed.
I have prepared a small testsuite to illustrate some of the typical situations that arise in application libraries.
At the end of the day, after alias elimination, each of these models has two unknowns: a power flow and a temperature. Everything else are aliases.
The following table reports: the start value of the temperature I would expect to see as a library developer, the start value I get with OMC v1.19.0-dev-256-g680066, and the start value I get with Dymola 2021x. Both tools select the temperature as iteration variable for the 2x2 nonlinear system.
Apparently Dymola does a better job at capturing my expectations, at 67% success, than OMC, at 33% success. I'm not 100% sure if this is due to a better implementation of what the Specification says, which BTW is non-normative, or if implements better heuristics. However, there are at least two cases where I get values that are obviously not the ones I would expect in both tools.
@henrikt-ma, feel free to add what System Modeller does.
In order to get what I'd like, I wrote some loosely formulated requirements. They need to be formalized into actual rules for the compiler, and possibly made more compact.