Problems with record call in Dymola - modelica

The following package 'RecordTest' (example to reproduce an error of a bigger model) contains a Record to define the structure of some data. Further in package 'DataDefintion' two sets of data are defined. Finally this data should be used in package 'UseOfData'. Herein the data sets are read and the sum of all arrays A is evaluated in the function 'FunctionWithData'.
The simulation of model 'FunctionCall' works fine in OpenModelica. In Dymola I get the error: 'For variable package constant RecordTest.UseOfData.ReadData[1].A the subscript RecordTest.UseOfData.ReadData.Index of an array variable is not an integer.'
Do I miss anything? The constant 'Index' is defined as an integer in record 'DataStructure'. Further the model runs in OpenModelica. I don't understand the error of Dymola.
Thanks in advance.
package RecordTest
record DataStructure
constant Integer Index;
Real A[Index];
end DataStructure;
package DataDefinition
constant DataStructure Set1(Index=2, A={1,2});
constant DataStructure Set2(Index=2, A={3,4});
end DataDefinition;
package UseOfData
constant Integer N=2;
constant DataStructure ReadData[N]={DataDefinition.Set1, DataDefinition.Set2};
function FunctionWithData
input Real b;
output Real Result;
protected
Real Array[2];
algorithm
Array := {sum(ReadData[1].A), sum(ReadData[2].A)};
Result := b*sum(Array);
end FunctionWithData;
model FunctionCall
parameter Real b=2;
Real FunctionResult;
equation
FunctionResult = FunctionWithData(b);
end FunctionCall;
end UseOfData;
end RecordTest;

A work-around is to rewrite the package as follows:
package RecordTest
record DataStructure
constant Integer Index;
Real A[:];
end DataStructure;
package DataDefinition
constant DataStructure Set1=DataStructure(Index=2, A={1.0,2.0});
constant DataStructure Set2=DataStructure(Index=2, A={3.0,4.0});
end DataDefinition;
package UseOfData
constant Integer N=2;
constant DataStructure ReadData[N]={DataDefinition.Set1, DataDefinition.Set2};
function FunctionWithData
input Real b;
output Real Result;
protected
Real Array[2];
algorithm
Array := {sum(ReadData[1].A), sum(ReadData[2].A)};
Result := b*sum(Array);
end FunctionWithData;
model FunctionCall
parameter Real b=2;
Real FunctionResult;
equation
FunctionResult = FunctionWithData(b);
end FunctionCall;
end UseOfData;
end RecordTest;
The issues are the "Index" used in A inside a package-constant array of records, and the modifiers instead of binding equation for the package-constant records Set1 and Set2.
(It will also be handled in a future version of Dymola, and I understand the answer is a bit late.)

Related

Converting arrays from signed to integer in VHDL?

I have declared an array
type datastream is array(0 to 10) of signed (5 downto 0);
For simulation, I want to display the array as integer-numbers
So I created
type datastream_int is array(0 to 10) of integer;
and
signal DIN_ARRAY: datastream;
signal DIN_ARRAY_int: datastream_int;
...
DIN_ARRAY_real <= datastream_int(DIN_ARRAY);
But it fails. How to convert it? Dont want to use a for loop
The numeric_std package, that I assume you are using, provides a to_integer function to convert from a single signed value to a single integer object. For an array, you're going to have to use a for loop. Something like this:
for i in DIN_ARRAY'range loop
DIN_ARRAY_int <= to_integer(DIN_ARRAY(i));
end loop;
You could also provide a conversion function (it will also contain a for loop)
function datastream_to_datastream_int( d : datastream ) return datastream_int is
variable r : datastream_int;
begin
for i in d'range loop
r(i) := to_integer(d(i));
end loop;
return r;
end function;
....
--no loop required
DIN_ARRAY_int <= datastream_to_datastream_int(DIN_ARRAY);
So, there will be a for loop somewhere.
Your code fails because you have attempted a type conversion, which is only allowed between similar types - ie. array or record types where the element types match between the two types.
PS. VHDL 2008 provides an integer_vector type in the std.standard library (which is included by default) which may help by allowing you to do this:
signal DIN_ARRAY_int: integer_vector(DIN_ARRAY'range);
If you did decide to keep datastream_int as per your original, you could type convert it to an integer_vector, because the types are similar:
my_iv <= integer_vector(DIN_ARRAY_int);

Conditional declaration by array of records

I try to create many components depending on the value of constant elements. These elements are organized in an array of records.
Dymola prints the translation log for the example below:
But I'm sure to use fixed conditions because I only perform allowed operations on constant values.
Here is the simple example of what I wantet to do:
model ConditionalComponent
type Enum = enumeration(one,two,three);
record Tmp
parameter Integer ID;
parameter Boolean active;
end Tmp;
record TmpNamed
parameter Enum name;
extends Tmp;
end TmpNamed;
function reorder
input TmpNamed inp[:];
output Tmp out[size(inp,1)];
algorithm
for elem in inp loop
out[elem.name] := Tmp(elem.ID, elem.active);
end for;
end reorder;
constant TmpNamed testIn[:] = {
TmpNamed(Enum.two,20,true),
TmpNamed(Enum.one,10,true),
TmpNamed(Enum.three,30,true)};
constant Tmp testOut1[:] = reorder({
TmpNamed(Enum.two,20,true),
TmpNamed(Enum.one,10,true),
TmpNamed(Enum.three,30,true)});
constant Tmp testOut2[:] = reorder(testIn);
constant Boolean active1 = testOut1[Enum.one].active;
constant Boolean active2 = testOut2[Enum.one].active;
Real t1=0 if testOut1[Enum.one].active;
//Real t2=0 if testOut2[Enum.one].active;
//Real t3=0 if active1;
//Real t4=0 if active2;
end ConditionalComponent;
The function reorder is intended to ease the management of large lists of named active components. Normally the constant testOut2 is used and created within the package ConditionalComponent. But for testing purposes ConditionalComponent is a model here. Actually I only want to use the line
Real t2=0 if testOut2[choice].active;
parameter Enum choice = Enum.one;
within other components, that have a parameter of type Enum. The declarations for t1, t3, t4 are only some tests that work, depending on what is left uncommented.
For example leaving the declaration for t1 and t3 uncommented works. But if one uses only the declaration for t1, it is not translated by Dymola.
The difference between t1 and t2 is, that the argument for reorder is passed directly or via the constant testIn.
I'm sure, that most parameter and constant prefixes are unnecessary and I tried hard to figure out the problem. But unfortunately I cannot decide whether Dymola is not working correctly or I did something wrong. And I've got no idea how to debug the translation process to figure it out by myself.
Can anyone tell me, what am I doing wrong?
Not something wrong, but it's just currently seen as too complicated and not handled.
A work-around is to split subscripting and element access:
constant Tmp testOut1_one=testOut1[Enum.one];
Real t1=0 if testOut1_one.active;

“Variability” error in model with function call

The following code
model FunctionCall
Boolean result;
function F
input Real p1;
output Boolean result;
algorithm
result :=p1 < 0.5;
end F;
algorithm
result :=F(time);
end FunctionCall;
(also described in http://www.modelica-forum.com/forums/index.php?showtopic=2) still throws an error in Dymola 2018FD01, while in OpenModelica it is accepted.
Is this wrong Modelica code or a Dymola bug?
Thanks in advance.
The model is incorrect.
3.8 "For an assignment v:=expr or binding equation v=expr, v must be declared to be at least as variable as expr"
Boolean variables are discrete-time expressions according to 3.8.3 "Discrete-time variables, i.e., Integer, Boolean, String variables and enumeration variables, as well as Real variables assigned in when-clauses"
F(time) is not a discrete-time expression, since 3.8.3 only includes "Function calls where all input arguments of the function are discrete-time expressions"
All according to Modelica 3.4.
The reason is that Boolean variables in models should only change at events, and the result of a function such as F(time) can neither guarantee that nor reliably generate events.
Hans answer is the correct one for your question.
Your unasked question may be how one can get the same behavior within the language specifications. Below I have provided one possible solution.
model FunctionCall
Boolean result;
function F
input Real p1;
output Integer result;
algorithm
result := if p1 < 0.5 then 1 else 0;
end F;
algorithm
result := if F(time) < 0.5 then false else true;
end FunctionCall;

Loop through modelica array fails

I am using openmodelica and I am trying to loop through an array in order to find the maximum value. I was able to reduce my code to a very simple test case that still gives the error. Is this something that I am doing wrong, or is this a bug in openmodelica? Here is a very simple case that does give the error:
package TestLoop
model ItemA
Real p;
end ItemA;
model ItemB
ItemA a[n];
parameter Integer n = 5;
Real p;
equation
for i in 1:n loop
a[i].p = time;
end for;
algorithm
for i in 1:n loop
p := a[i].p;
end for;
end ItemB;
end TestLoop;
The problem is in my algorithm section. Here is the error that I am getting:
TestLoop.ItemB.c:155:13: warning: implicit declaration of function '$Pa$lB' is invalid in C99 [-Wimplicit-function-declaration]
$Pp = $Pa$lB(modelica_integer)$Pi$rB$Pp;
^
TestLoop.ItemB.c:155:20: error: unexpected type name 'modelica_integer': expected expression
$Pp = $Pa$lB(modelica_integer)$Pi$rB$Pp;
^
1 warning and 1 error generated.
Any suggestions for why this might be, or how I can work around it? If I replace the assignment with a fixed value, p:=a[1].p;, the code does run (although that is not useful to me). What I ultimately want to do in the algorithm section is find the largest value of a[n].p, where I do have an equation section that does useful calculations into the array of items.
Yes, the code generation is an error of OpenModelica (it does not like unknown array indexes). Your problem is very easy to solve in a single line though (one of the following):
p = max(r for r in a.p);
p = max(a.p);

Load a record using dot notation and strings from an array in Modelica

Is there a way to load a record in Modelica, and manipulating part of the record directory with a string from an array?
I included the minimal working example below, in summary, I have two different records with parameter values named Cylinder and Board and I have an array containing their names. I would like to access one set of parameter values in a function, depending on the user input "ShapeNumber". So when the user is putting a 1, load the parameters from the Cylinder record, when the user puts a 2, load the parameters from the Board record. Ideally, I would like to add the string "Cylinder" or "Board" into the dot notation for loading the record, as indicated in the code below.
package Test_things
record General_parameters
parameter Real Length "Length in m";
end General_parameters;
record Cylinder_parameters
extends General_parameters;
parameter Real Diameter "Diameter in m";
end Cylinder_parameters;
record Board_parameters
extends General_parameters;
parameter Real Width "Width in m";
parameter Real Depth "Depth in m";
end Board_parameters;
record Parameter_values
constant Test_things.Cylinder_parameters Cylinder(Length=10, Diameter=0.5);
constant Test_things.Board_parameters Board(
Length=20,
Width=1,
Depth=0.01);
constant String[2] ShapesArray = {"Cylinder","Board"};
end Parameter_values;
function Length_tester
input Integer ShapeNumber; //Which shape from within the shape array is tested
output Boolean LongEnough;
Test_things.Parameter_values Values; //Gives Values.Cylinder, Values.Board, Values.ShapesArray
protected
String ShapeName;
Real Length;
algorithm
//Load correct shape
ShapeName := Values.ShapesArray[ShapeNumber];
// This is what I would like: Length := Values.{ShapeName}.Length;
// which results in one of the two lines of code below.
// Length := Values.Cylinder.Length;
// Length := Values.Board.Length;
// This works for now, but has to be adjusted whenever a new shape is used
if ShapeName == "Cylinder" then
Length := Values.Cylinder.Length;
elseif ShapeName == "Board" then
Length := Values.Board.Length;
end if;
if Length > 15 then
LongEnough := true;
else
LongEnough := false;
end if;
end Length_tester;
model Main
Boolean LongEnoughCylinder;
Boolean LongEnoughBoard;
equation
// Test Cylinder
LongEnoughCylinder = Length_tester(1);
// Test Board
LongEnoughBoard = Length_tester(2);
end Main;
end Test_things;
I found the work around with the if-statement, but when I add a new record, I have to update that if-statement in every function I have, rather than only updating the array with the names of the records.
Thank you for any help!
What you try to do is currently not possible, as Modelica does not support dynamic creation of class paths. All class paths must be hard coded.
Therefore your if/else workaround is a legit solution. To keep the maintainability low, you could create a function getLength with contains the if/else logic and all your other functions will use this one.
Below you can find the solution which I would use. Everything is made as generic as possible, so you can use an arbitrary number of shapes of any kind. To do so, there is the generic shape type Shapewith all possible shape parameters. Then there are the specialized shapes Board and Cylinder, which set not needed parameters on 0 in combination with the keyword final, so users can not modify them. This allows to create arrays of shapes, containing the basic shape type, as well as the specialized shapes. The testLength function will then be very simple, accepting an array of shapes. The downside of this solution is (for you as developer), that new shapes with new parameter require an update the basic shape type and the specialized shapes.
package Test_things
record Shape "Generic shape with all possible parameters"
import SI = Modelica.SIunits;
// Common parameters
parameter ShapeTypes shapeType "Kind of shape";
parameter SI.Length length;
// Board parameters
parameter SI.Length width;
parameter SI.Length depth;
// Cylinder parameters
parameter SI.Diameter diameter;
end Shape;
type ShapeTypes = enumeration(Cylinder, Board);
record Cylinder "Cylinder shape with unneeded parameters set on final 0"
extends Shape(final shapeType=ShapeTypes.Cylinder, final width=0, final depth=0);
end Cylinder;
record Board "Board shape with unneeded parameters set on final 0"
extends Shape(final shapeType=ShapeTypes.Board, final diameter=0);
end Board;
function testLength "Check if length of a specific shape in an array of shapes is bigger than 15"
input Integer shapeNumber "Shape of interest";
input Shape shapes[:] "Array of shapes";
output Boolean longEnough;
algorithm
longEnough := shapes[shapeNumber].length > 15;
end testLength;
model Main
constant Shape shapes[:] = {
Board(length=20, width=1, depth=0.01),
Cylinder(length=1, diameter=2)};
// note: this works in OpenModelica only with the prefix constant, parameter does not compile
// in Dymola it works with both
Boolean longEnoughCylinder;
Boolean longEnoughBoard;
equation
// Test Cylinder
longEnoughCylinder = testLength(1, shapes);
// Test Board
longEnoughBoard = testLength(2, shapes);
end Main;
end Test_things6;
Some notes on style:
all classes except of function should be written in upper case
all instances, parameters, etc. should be written in lower case
instead of writing the unit into the comment, use SI units