Array Expandable Connector Issue - modelica

I'm trying to use an array expandable connector in the following way:
model MWE
expandable connector ControlBus
extends Modelica.Icons.SignalBus;
end ControlBus;
ControlBus controlBus[1];
Modelica.Blocks.Math.Gain gain(k=1);
equation
connect(gain.u, controlBus[1].a);
end MWE;
...however Dymola throws an error about not finding member 'a' in record extension.
Strangely, if i declare an integer parameter after the controlBus declaration (not before!) and use that to specify the size of the controlBus it works as expected with no errors:
model MWE
expandable connector ControlBus
extends Modelica.Icons.SignalBus;
end ControlBus;
ControlBus controlBus[k];
Modelica.Blocks.Math.Gain gain(k=1);
parameter Integer k=1;
equation
connect(gain.u, controlBus[1].a);
end MWE;
My main question is whether having an array expandable connector is allowed in Modelica? If so, any issues with my first approach or does Dymola have some bug (that apparently happens to be overcome by the workaround in the second approach)?
EDIT: adding another related example where the declaration order appears to make a difference with expandable connectors:
model MWE
expandable connector ControlBus
Real variable;
end ControlBus;
ControlBus controlBus;
Modelica.Blocks.Sources.RealExpression realExpression(y=controlBus.variable);
end MWE;
Here Dymola gives a warning on check but will compile. However, had I declared controlBus after realExpression it wouldn't have complained.

There is nothing making this example illegal in Modelica; a is an undeclared member of the expandable connector component controlBus[1].
https://specification.modelica.org/maint/3.5/connectors-and-connections.html#expandable-connectors
It seems that Dymola needs to be improved to handle this case, and assuming nothing odd happens that will be done in the next regular release.

Related

Internal error in code generation for pre

I try to avoid the algebraic loop in Modelica by using pre operator, but when I use something like pre(x>0.5), there will be an error of Internal error in code generation for pre.
And if I use pre(cond), where cond is a boolean type variable, there won't be any error.
My question is: Is there some regulation of pre operator which requires that I could NOT use expressions within pre operator.
Here are the code and screenshot:
model WithAlgebraicLoop_Wrong2
"Demonstration of how to avoid generating algebraic loop,
but end up with internal error in code generation for pre"
Real x,y(start=1,fixed=true);
equation
when pre(x>0.5) then
y=1*time;
end when;
x=sin(y*10*time);
end WithAlgebraicLoop_Wrong2;
model WithAlgebraicLoop_Right "Demonstration of how to avoid generating algebraic loop"
Real x,y(start=1,fixed=true);
Boolean cond;
equation
cond=x>0.5;
when pre(cond) then
y=1*time;
end when;
x=sin(y*10*time);
end WithAlgebraicLoop_Right;
You can read in the Modelica Language Specification (section 3.7.3 on event related operators -> table) that the argument of pre needs to be a variable, but not an expression.

Correct syntax for redeclaration of replaceable parameters for array components

I have developed a model which uses replaceable records quite extensively. The parameters are passed down to arrays of components. I tried two approaches to do this, but neither of them work in all my test environments SimulationX (SimX), OpenModelica (OM), Dymola and Wolfram SystemModeler (WSM).
Let's consider a simplified package to illustrate my problem. Depending on the chosen sumbodel, the model calculates either the sum or the product of a replaceable record's parameters. I tried two approaches for changing the record within the sub-models.
approach 1: redeclare sub-model record by modification (MOD)
approach 2: redeclare a corresponding inner record to change the outer record within the sub-model (IO)
The package contains an example with 4 models with different record/sub-model combinations for MOD and IO each.
Both approaches work fine in SimX
Dymola correctly simulates with the MOD approach, but produces errors for IO
OM can simulate the model, but does not produce the correct results for all scenarios
WSM produces warning messages concerning the records I mentioned, but simulates the correct results.
I removed the example and annotations from the shown code for readability, but the full package can be downloaded here: https://hessenbox.tu-darmstadt.de/getlink/fiEgNjMEBZpSyJWHKafmoYYS/RedeclareTestPackage.rar
package RedeclareTestPackage "redeclarationTest"
partial record datasetPartial "NewRecord1"
parameter Real whatever;
parameter Integer idontcare;
end datasetPartial;
record datasetA "record A"
extends datasetPartial(
whatever=1.0,
idontcare=2);
end datasetA;
record datasetB "record B"
extends datasetPartial(
whatever=2.0,
idontcare=3);
end datasetB;
partial model partialSubModelType_Mod "NewModel1"
replaceable parameter datasetA subModelDataset constrainedby datasetPartial;
Real C;
end partialSubModelType_Mod;
model subModelTypeMultiply_Mod "sub-model type Multiply"
extends partialSubModelType_Mod;
equation
C=subModelDataset.whatever*subModelDataset.idontcare;
end subModelTypeMultiply_Mod;
model subModelTypeAdd_Mod "sub-model type Add"
extends partialSubModelType_Mod;
equation
C=subModelDataset.whatever+subModelDataset.idontcare;
end subModelTypeAdd_Mod;
partial model partialSubModelType_innerOuter "NewModel1"
outer replaceable parameter datasetA subModelDataset constrainedby datasetPartial;
Real C;
end partialSubModelType_innerOuter;
model subModelTypeMultiply_innerOuter "sub-model type Multiply"
extends partialSubModelType_innerOuter;
equation
C=subModelDataset.whatever*subModelDataset.idontcare;
end subModelTypeMultiply_innerOuter;
model subModelTypeAdd_innerOuter "sub-model type Add"
extends partialSubModelType_innerOuter;
equation
C=subModelDataset.whatever+subModelDataset.idontcare;
end subModelTypeAdd_innerOuter;
model modificationModel "NewModel1"
replaceable parameter datasetA modelData constrainedby datasetPartial;
replaceable subModelTypeAdd_Mod submodel[1,1] constrainedby
partialSubModelType_Mod(each subModelDataset=modelData);
Real test=submodel[1,1].C;
end modificationModel;
model innerOuterModel "NewModel1"
inner replaceable parameter datasetA subModelDataset constrainedby datasetPartial;
replaceable subModelTypeAdd_innerOuter submodel[1,1] constrainedby
partialSubModelType_innerOuter;
Real test=submodel[1,1].C;
end innerOuterModel;
end RedeclareTestPackage;
The error message in OM reads:
Translation Warning [RedeclareTestPackage: 39:3-39:84]: An inner
declaration for outer component subModelDataset could not be found and
was automatically generated.
Strangely, it is only the combination of redeclaring both the dataset and the sub-model with inner/outer which produces the message and fails to simulate correctly. Everything else works fine.
WSM produces error messages for both approaches. The MOD approach producing e.g.:
Warning: In variable or component
'MOD_ADD_1and2.submodel.subModelDataset': Duplicate modification of
element =(untyped) 1.0 and each = (typed)1.0 DAE.PROP(Real,
C_CONST), value: 1.0 on component whatever.
Warning: Duplicate
modification of element =(untyped) 1.0 and each = (typed)1.0
DAE.PROP(Real, C_CONST), value: 1.0 on component whatever.
A WSM example for the IO approach reads:
Warning: Ignoring the modification on outer element:
IO_ADD_1and2.submodel[1,1].subModelDataset .
All models simulate the correct results in WSM though. Dymola only works for the modification examples, the inner/outer redeclaration producing errors like:
Replaceable must be a subtype of constraining class. But different
inner/outer qualifiers for component subModelDataset. In declaration
of component subModelDataset: File:
C:/.../RedeclareTestPackage/RedeclareTestPackage.mo, line 39 Original
declaration of subModelDataset: File:
C:/.../RedeclareTestPackage/RedeclareTestPackage.mo, line 39 Context:
RedeclareTestPackage.partialSubModelType_innerOuter.subModelDataset
If someone could enlighten me, I would be very grateful. My actual model works just fine in SimX, but in my opinion one of the main reasons for Modelica is to develop vendor independent models, to be able to share them with a large community.
Best regards
Julian

Connect parameter variables in expandable connector

Two models are connected via an (empty) expandable connector.
One of the two, makes a connection between the expandable connector and a parameter variable.
I didn't expect any issue. On the contrary, I had some issues:
OpenModelica compiles Version 2, but not Version 1
[bus_param_out]: Failed to instantiate equation connect(controlBus.num, numberPar);
Translation Error: Error occurred while flattening model bus_param
Dymola does not compile either Version 1 or Version 2
Connect does not refer to connectors in connect(bus_in.controlBus.num, bus_in.num);
but bus_in.num is a Real and Modelica Specifications says
All components in an expandable connector are seen as connector instances even if they are not declared as such [i.e. it is possible to connect to e.g. a Real variable].
What am I doing wrong? Am I missing something?
My final goal is to link a parameter variable to an expandable connector (e.g. inform different vehicle components about the number of battery cells, let's say) without the need of an additional redundant variable. Is this possible?
The test code is the following:
bus_param.mo
model bus_param
bus_param_in bus_in;
bus_param_out bus_out;
equation
connect(bus_in.controlBus, bus_out.controlBus);
end bus_param_custom;
bus_param_in.mo
model bus_param_in
Real num;
bus controlBus;
equation
connect(controlBus.num, num);
end bus_param_in;
bus_param_out.mo - Version 1
model bus_param_out
parameter Real numberPar = 3.0;
bus controlBus;
equation
connect(controlBus.num, numberPar);
end bus_param_out;
bus_param_out.mo - Version 2
model bus_param_out
parameter Real numberPar = 3.0;
Real number;
bus controlBus;
equation
number = numberPar;
connect(controlBus.num, numberPar);
end bus_param_out;
bus.mo
expandable connector bus
end bus;
f.wue already showed how to write a parameter to the bus.
This post additionally explains how to read the value without increasing the variability (so it stays a parameter).
To make its usage easier, here is the complete code of a demo package to show how to read and write parameters on busses.
It works with Dymola 2019 FD01 in pedantic mode and OMEdit v1.13.2.
package ParmeterToBus
expandable connector bus
end bus;
model bus_param_out
parameter Real numberPar;
Modelica.Blocks.Sources.Constant helper(k=numberPar);
bus controlBus;
equation
connect(controlBus.number, helper.y);
end bus_param_out;
model bus_param_in
Modelica.Blocks.Interfaces.RealOutput buffer;
bus controlBus;
final parameter Real num(fixed=false); // users should not modify it, hence its final
initial equation
num = buffer;
equation
connect(buffer, controlBus.number);
end bus_param_in;
model example
bus_param_in bus_in;
bus_param_out bus_out(numberPar=7);
equation
connect(bus_in.controlBus, bus_out.controlBus);
end example;
end ParmeterToBus;
Note that the implementation is far from being straightforward.
Some tweaks are necessary along with helper classes to overcome the following restrictions:
Only connectors can be used in connect statements.
So we need
an instance of an constant block to write the value
(helper in the code above)
an instance of an RealOutput connector to read the value
(buffer in the code above)
Models and blocks are not allowed to have the prefix parameter.
Therefore wee need the constant block to write the value, we cannot use a RealOutput connector here.
For parameters usually an initial equation is automatically generated from its binding equation.
To prevent this, we have to set (fixed=false). This allows us to assign the parameter in the initialization phase with the value of a variable of higher variability - in our case the buffer.
You could use
Modelica.Blocks.Interfaces.RealOutput num
to declare a Real that can be used in a connect statement.
EDIT:
As far as i know, connecting a parameter to a connector is not possible. Dymola will yield the error:
Connect does not refer to connectors in connect
The official way would be to use Modelica.Blocks.Sources.Constant, which is equivalent to RealOutput. You can directly use a parameter like this:
model bus_param_out
parameter Real number = 3;
Modelica.Blocks.Sources.Constant num_con(k=number);
bus controlBus;
equation
connect(controlBus.num, num_con.y);
end bus_param_out;
When using expandable connectors and connect these connectors, you have to make sure to set bus.numonly once. Everything else will result in an error.
Try connecting everything with the graphical interface, that will maybe clear things up.
You can use the expandable connector outside of connect like this:
model bus_param_out
Real number_of_bus;
parameter Real number = 3;
Modelica.Blocks.Sources.Constant num_con(k=number);
bus controlBus;
equation
connect(controlBus.num, num_con.y);
number_of_bus = controlBus.num;
end bus_param_out;
But trying to declare parameter Real number_of_bus will result in the following error:
The variability of the definition equation: number_of_bus =
controlBus.num; is higher than the declared variability of the
variables.
because the connector is time-variant and the parameter constant.

How to update the visibility of an object based on a parameter

Connections have at least two important variables. The flow variable and not-flow variable (and then stream stuff but lets not talk about those). For clarity I will reference the Fluid connector and its variables m_flow (flow variable) and p (not flow variable).
When your building components it is important to specify if that component is setting the value for the m_flow or p. For example, you do not want to connect two pressure loss components (sets m_flow) together.
The fluid connectors in MSL are defined as port_a (design inlet) and port_b (design outlet). To specify if a port sets m_flow or not, DynamicPipe opts to use the PartialTwoPort component that has an object (black ellipse) in the icon layer that toggles its visibility based on a parameter (port_a_exposesState) which can be modified when PartialTwoPort is extended (i.e., PartialTwoPortFlow).
However, this feature does not work. The parameter will not change its display when the parameter is changed (i.e., the black ellipse on DynamicPipe never goes away).
Below is a simple example demonstrating the concept. When the model "RunMe" is simulated the parameter showBall = false because number <> 1. However, the ball is still visible.
Partial Model setting the object that should appear/disappear:
partial model ballIcon
// input Boolean showBall; // Tried this as well to no avail.
protected
parameter Boolean showBall = true;
annotation (Icon(coordinateSystem(preserveAspectRatio=false), graphics={
Ellipse(
extent={{-40,40},{40,-40}},
lineColor={0,0,0},
fillPattern=FillPattern.HorizontalCylinder,
fillColor={255,255,0},
visible=showBall)}),
Diagram(coordinateSystem(
preserveAspectRatio=false)));
end ballIcon;
Extending model:
model extendsBallIcon
extends ballIcon(showBall=(number==1));
parameter Real number = 1;
end extendsBallIcon;
Model that should show a ball that appears or disappears based on "number":
model RunMe
extendsBallIcon Ball(number=3)
annotation (Placement(transformation(extent={{-10,-10},{10,10}})));
end RunMe;
Is there something that can be corrected to this approach so the GUI of a component when used (i.e., on the diagram layer) will work? Or do connectors GUI need to capture the flow/not-flow element that is defined by a component to assist in model usage (i.e., change port_a from description design inlet to defined flow variable).
Using Dymola 2017 (tested with the same results on Dymola 2016 as well)
Update:
Knowing the simple case works using DynamicSelect leads me to a real application. The following change appears to cause the icon to not toggle its visiblity.
In extendsBallIcon replacing:
extends ballIcon(showBall=(number==1));
parameter Real number = 1;
with
// Boolean Example
extends ballIcon(showBall=(number==true));
final parameter Boolean number = (modelStructure==Modelica.Fluid.Types.ModelStructure.av_b);
parameter Modelica.Fluid.Types.ModelStructure modelStructure = Modelica.Fluid.Types.ModelStructure.av_b;
Modelica language specification states in section 18.6.6: "Any value (coordinates, color, text, etc) in graphical annotations can be dependent on class variables using the DynamicSelect expression." That is, visible=DynamicSelect(true, showBall) in your example will display the ellipse only if showBall is true.

Connection Restrictions on Input and Output Connectors

I would like to enforce that the user cannot connect an input to an input. I expected the code below to give a compile-time error but it gives no error. How can I fix this?
Another issue is the package-global compile-time constant C. It is sort of a parameter, and it should be provided by the user of the package. How should this be implemented in Modelica?
package Pkg
constant Integer C=3;
connector Connector
Real x[C];
end Connector;
connector InConn = input Connector;
connector OutConn = output Connector;
class Base
InConn[:] inlet;
OutConn[:] outlet;
end Base;
class A
extends Base;
redeclare InConn[1] inlet;
redeclare OutConn[1] outlet;
end A;
end Pkg;
model Test
import Pkg.*;
A p;
A q;
equation
connect(p.inlet[1], q.inlet[1]);
end Test;
There are a couple of problems here. The main one is that your redeclarations in A are not correct. They should be modifications on the extends clause. But also note they are not even necessary since they don't actually change anything. Specifying sizes should be done through parameters.
Similarly, the constant really needs to be a parameter of your Connector definition. The Modelica compiler should throw an error if you connect two connectors with different sizes (specifically, it should generate an assertion on the values of any parameters in the connection set).
I don't have a Modelica compiler installed on this machine, but I suggest you try this and see if this works better for you:
package Pkg
connector Connector
parameter Integer C=3;
Real x[C];
end Connector;
connector InConn = input Connector;
connector OutConn = output Connector;
class Base
parameter Integer ni;
parameter Integer no;
InConn[ni] inlet;
OutConn[no] outlet;
end Base;
class A
extends Base(ni=1, no=1);
end A;
end Pkg;
model Test
import Pkg.*;
A p;
A q;
equation
connect(p.inlet[1], q.inlet[1]);
end Test;
Hopefully that will get things into a state where the compiler will generate the correct error. The semantics of Modelica are such that connection of two inputs should trigger an error (in fact, that is the fundamental restriction of input and output connectors).