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);
Related
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;
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;
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.)
I'm working with someone else's code and I am unfamiliar with try/catch so I made a small, similar example. On line 11, if I write error(''), it doesn't seem to catch the error and increase the index j. However, writing error(' ') or error('bad!') does.
So does having an error with an empty string ignore the error, or am I doing something wrong?
% Just a file to understand the Matlab command try/catch
M = 3;
j = 1;
k = [Inf, 5, 4];
while M>0
try
M = M-1
u = k(j)
if (isinf(u)||isnan(u)), error(''), end;
catch
j = j+1
end
end
Yes, error('') and error([]) and error(struct([])) all do not actually display an error message and abort running code. I personally consider the use of the single string argument version of error to be bad practice in any real code. You should use always use both a 'MSGID' and a 'ERRMSG' when writing errors for your functions, e.g.
error('FunctionName:SubFunctionName:ErrorMSGID','Error message to be printed.')
Alternatively, you can use MException objects in conjuction with throw, rethrow, and throwAsCaller, which allow you to reuse error information. More here.
It is odd, but it's in the documentation for error, for the error('msgString') syntax:
All string input arguments must be enclosed in single quotation marks. If msgString is an empty string, the error command has no effect.
Similarly, if using the error(msgStruct) syntax:
If msgStruct is an empty structure, no action is taken and error returns without exiting the function.
if you have a look to the try documentation you can have an example.
Else want you want for your code it :
M = 3;
j = 1;
k = [Inf, 5, 4];
while M>0
try
M = M-1
u = k(j)
if (isinf(u)||isnan(u)), error(''), end;
catch
disp('I catch an error!');
j = j+1
end
end
Because If you never get an error in your code, it will never go in the catch. So by including error('');, it just to say, go execute the statement in the catch.
But you can just modify your code by replacing the error() by the statements into your catch like this :
while M>0
M = M-1
u = k(j)
if (isinf(u)||isnan(u)), j = j+1, end;
end
EDIT
If you take a look in the documentation, you can found this :
% ERROR(MSGSTRUCT) reports the error using fields stored in the scalar
% structure MSGSTRUCT. This structure can contain these fields:
%
% message - Error message string
% identifier - See MESSAGE IDENTIFIERS, below
% stack - Struct similar to the output of the DBSTACK function
%
% If MSGSTRUCT is an empty structure, no action is taken and ERROR
% returns without exiting the program. If you do not specify the
% stack, the ERROR function determines it from the current file and line.
So no action is taken as you can read. And nothing, so catch don't get any informations.
Not sure why you need it, but here is how it works.
error function does not throw an error with empty string or empty vector ([]) as an argument.
If you don't specify argument at all the error function itself generates the error "Not enough arguments". So it will go to catch.
Another way is to specify an empty structure as an argument.
s = struct();
error(s)
In this case, the error will be generated, but the code will not stop and in general flow you will hear no beep. In your case it should go to catch.
So, we are trying to execute the following code. The two if statements are executing, however, the inside if statements are failing to execute (we verified this by not suppressing the output). Is there a reason why? Or are we just not able to reach this state?
Specifications
The input is as follows: v is a vector of int values and c is a integer. c must be less than or equal to one of the values within v
The problem that we are trying to solve with this algorithm is as follows:
Given a cash register, how does one make change such that the fewest coins
possible are returned to the customer?
Ex: Input: v = [1, 10, 25, 50], c = 40. Output O = [5, 1, 1, 0]
We are just looking for not a better solution but more of a reason why that portion of the code is not executing.
function O = changeGreedy(v,c)
O = zeros(size(v,1), size(v,2));
for v_item = 1:size(v,2)
%locate largest term
l_v_item = 1
for temp = 2:size(v,2)
if v(l_v_item) < v(temp)
l_v_item = temp
end
end
%"Items inside if statement are not executing"
if (c > v(l_v_item))
v(l_v_item) = -1 %"Not executing"
else
O(l_v_item) = idivide(c, v(l_v_item)) %"Not executing"
c = mod(c, v(l_v_item)) %"Not executing"
end
end
If c or v are not integers, i.e. class(c) evaluates to double, then I get the following error message
??? Error using ==> idivide>idivide_check at 66
At least one argument must belong to an integer class.
Error in ==> idivide at 42
idivide_check(a,b);
and the program stops executing. Thus, the inside of the second statement never executes. In contrast, if, say, c is an integer, for example of class uint8, everything executes just fine.
Also: what are you actually trying to achieve with this code?
Try to do this operation on your input data:
v = int32([1, 10, 25, 50]), c = int32(40)
and run again, at least some portions of your code will execute. There is an error raised by idivide, which apparently you missed:
??? Error using ==> idivide>idivide_check at 67
At least one argument must belong to an integer class.
Error in ==> idivide at 42
idivide_check(a,b);
Indeed, idivide seems to require that you have actual integer input data (that is, class(c) and class(v) both evaluate to an integer type, such as int32).