Array indexing with boolean array - modelica

Given a Real array (e.g. myArray) and a Boolean array (e.g. myMask), I'd like to have:
if myMask[i] == true then myArray[i] = myValueTrue
if myMask[i] == false then myArray[i] = myValueFalse
This does work
model BooleanIndexing
parameter Boolean myMask[3] = {false, true, true};
parameter Boolean myMask_negated[3] = {true, false, false};
Real myArray[3];
parameter Real myValueTrue = 3.0;
parameter Real myValueFalse = 5.0;
equation
myArray[Modelica.Math.BooleanVectors.index(myMask)] = fill(myValueTrue, Modelica.Math.BooleanVectors.countTrue(myMask));
myArray[Modelica.Math.BooleanVectors.index(myMask_negated)] = fill(myValueFalse, Modelica.Math.BooleanVectors.countTrue(myMask_negated));
end BooleanIndexing;
but this does not
model BooleanIndexing
parameter Boolean myMask[3] = {false, true, true};
parameter Boolean myMask_negated[3] = not myMask;
Real myArray[3];
parameter Real myValueTrue = 3.0;
parameter Real myValueFalse = 5.0;
equation
myArray[Modelica.Math.BooleanVectors.index(myMask)] = fill(myValueTrue, Modelica.Math.BooleanVectors.countTrue(myMask));
myArray[Modelica.Math.BooleanVectors.index(myMask_negated)] = fill(myValueFalse, Modelica.Math.BooleanVectors.countTrue(myMask_negated));
end BooleanIndexing;
The only difference is how I initialized myMask_negated.
The errors are, in OpenModelica:
[BooleanIndexing: 9:3-9:139]: Illegal subscript Modelica.Math.BooleanVectors.index({myMask_negated[1], myMask_negated[2], myMask_negated[3]}) for dimensions 3 in component myArray[Modelica.Math.BooleanVectors.index(myMask_negated)].
[BooleanIndexing: 9:3-9:139]: Variable myArray[Modelica.Math.BooleanVectors.index(myMask_negated)] not found in scope BooleanIndexing.
Error occurred while flattening model BooleanIndexing
and in Dymola2018
Translation of BooleanIndexing:
Failed to expand myArray[Modelica.Math.BooleanVectors.index(myMask)].
Errors or failure to expand the equation:
myArray[Modelica.Math.BooleanVectors.index(myMask)] = fill(myValueTrue, Modelica.Math.BooleanVectors.countTrue(myMask));
Found in class BooleanIndexing, C:/workspace/modelica_vehicle/modelica_test/BooleanIndexing.mo at line 8.
Errors or failure to expand vector or matrix expressions.
Translation aborted.
Direct boolean indexing myArray[myMask] seems not to be the solution here.
I cannot see why they are failing and if there are any more elegant solutions.

Both of your versions do not guarantee that the correct number of equations are generated.
myValueTrue and myValueTrue_negated are both parameters, so the user can change the values of the vectors to something which is not complementary.
Hence, I would have recommended to set
final parameter Boolean myMask_negated[3] = not myMask;
but this also does not work, neither in Open Modelica nor in Dymola.
So instead, I recommend to delete myMask_negated and use for loops instead.
Either with two seperate ones:
model BooleanIndexing
parameter Boolean myMask[3] = {false, true, true};
Real myArray[3];
parameter Real myValueTrue = 3.0;
parameter Real myValueFalse = 5.0;
equation
for i in Modelica.Math.BooleanVectors.index(myMask) loop
myArray[i] = myValueTrue;
end for;
for i in Modelica.Math.BooleanVectors.index(not myMask) loop
myArray[i] = myValueFalse;
end for;
end BooleanIndexing;
or with a single for loop using an array constructor, as shown here:
model BooleanIndexing2
parameter Boolean myMask[3] = {false, true, true};
Real myArray[3];
parameter Real myValueTrue = 3.0;
parameter Real myValueFalse = 5.0;
equation
myArray = {if value then myValueTrue else myValueFalse for value in myMask};
end BooleanIndexing2;

Related

Use matlab script to check symbol types in state chart

I am writing a script that will open a simulink model and check for all the logical comparison data types. I have four comparisons in my model, two of which are logical operators, the other two are condition statements in the state chart shown below.
Using a script I can check the data types of the logical operators shown below but I'm having trouble finding documentation on how to check the symbol data types from the state chart.
load_system('comparison.slx');
RelOps = find_system('comparison','BlockType','RelationalOperator');
for j = 1:numel(RelOps)
currRelOp = get_param(RelOps(j),'Operator');
if strcmp(currRelOp,'==')
currpc = get_param(RelOps(j),'PortConnectivity');
currpc = currpc{1,1};
srcblktype = get_param(currpc(2).SrcBlock,'BlockType');
if strcmp(srcblktype,'Constant')
srcblkdatatype = get_param(currpc(2).SrcBlock,'OutDataTypeStr');
end
end
I thought I would post this up on stack overflow while continuing my search. So far I have a script which can find all the variable names
compList = {'==' '!=' '<' '>' '<=' '>='};
charts = find(sfroot,'-isa','Stateflow.Chart');
st = find(charts(1),'-isa','Stateflow.Transition');
inputList = {};
for i = 1:numel(st)
condStr = st(i).LabelString;
if ~strcmp(condStr,'')
if contains(condStr, compList)
splitStr = split(condStr,compList);
for j = 1:numel(splitStr)
inputStr = char(splitStr(j));
inputStr = erase(inputStr,{' ','[',']'});
inputList = [inputList {inputStr}];
end
end
end
% For loop searching for data types
end

How to create a named constant in the SystemVerilog generate block?

I need to transmit a set of values between the SystemVerilog and VHDL code. To do that, I have to flatten the SV structures into the bit vectors, transmit the bit vectors, and rebuild the structures at the VHDL side.
Unfortunately, I have faced a serious problem at the SV side. My "flattener" has the following code:
generate
for(genvar i=0;i<nlinks;i++)
for(genvar j=0;j<2;j++)
begin
const int base = 2 * i + j;
assign vf[base] = lct_aligned[i][j].vf;
assign hs[(8*base+7):8*base] = lct_aligned[i][j].hs;
assign wg[(7*base+6):7*base] = lct_aligned[i][j].wg;
assign ql[(4*base+3):4*base] = lct_aligned[i][j].ql;
assign cp[(4*base+3):4*base] = lct_aligned[i][j].cp;
assign lr[base] = lct_aligned[i][j].lr;
assign bc0[base] = lct_aligned[i][j].bc0;
assign bx0[base] = lct_aligned[i][j].bx0;
assign ser[base] = lct_aligned[i][j].ser;
assign cid[(4*base+3):4*base] = lct_aligned[i][j].cid;
end
endgenerate
Unfortunately, it gives "base is not a constant error" in each "assign" line.
When I replace "base" with "(2*i+j)" the code compiles correctly, but is significantly less visible and mantainable.
generate
for(genvar i=0;i<nlinks;i++)
for(genvar j=0;j<2;j++)
begin
//const int base = 2 * i + j;
assign vf[(2*i+j)] = lct_aligned[i][j].vf;
assign hs[(8*(2*i+j)+7):8*(2*i+j)] = lct_aligned[i][j].hs;
assign wg[(7*(2*i+j)+6):7*(2*i+j)] = lct_aligned[i][j].wg;
assign ql[(4*(2*i+j)+3):4*(2*i+j)] = lct_aligned[i][j].ql;
assign cp[(4*(2*i+j)+3):4*(2*i+j)] = lct_aligned[i][j].cp;
assign lr[(2*i+j)] = lct_aligned[i][j].lr;
assign bc0[(2*i+j)] = lct_aligned[i][j].bc0;
assign bx0[(2*i+j)] = lct_aligned[i][j].bx0;
assign ser[(2*i+j)] = lct_aligned[i][j].ser;
assign cid[(4*(2*i+j)+3):4*(2*i+j)] = lct_aligned[i][j].cid;
end
endgenerate
Is there any way to define constants for complex expressions in the SystemVerilog generate blocks?
A const variable is not really a constant. It's a variable with a write once at run-time value. A parameter or localparam is a compile time constant. Simply replace const with parameter.

codegen error: The left-hand side has been constrained to be non-complex, but the right-hand side is complex

I am new to matlab and am trying to compile legacy matlab code into C. I come across the following error when doing so:
??? The left-hand side has been constrained to be non-complex, but the
right-hand side is complex. To correct this problem, make the
right-hand side real using the function REAL, or change the initial
assignment to the left-hand side variable to be a complex value using
the COMPLEX function.
The code that it complains on is in the comments of the code below:
function [z_out,ovf_flag,ovf_cnt] = fxpt_sgn_saturate(z_in,Nb_out)
Nb_out=(Nb_out<=0)+(Nb_out>0)*Nb_out;
max_val = 2^(Nb_out-1)-1;
min_val = -2^(Nb_out-1);
ovf_cnt = 0;
tmp_ind = find(real(z_in) > max_val);
z_in(tmp_ind) = max_val+1j*imag(z_in(tmp_ind)); // ERROR OCCURS HERE
ovf_cnt = ovf_cnt + numel(tmp_ind);
tmp_ind = find(real(z_in) < min_val);
z_in(tmp_ind) = min_val+1j*imag(z_in(tmp_ind));
ovf_cnt = ovf_cnt + numel(tmp_ind);
tmp_ind = find(imag(z_in) > max_val);
z_in(tmp_ind) = real(z_in(tmp_ind))+1j*max_val;
ovf_cnt = ovf_cnt + numel(tmp_ind);
tmp_ind = find(imag(z_in) < min_val);
z_in(tmp_ind) = real(z_in(tmp_ind))+1j*min_val;
ovf_cnt = ovf_cnt + numel(tmp_ind);
z_out = z_in;
ovf_flag = ~(ovf_cnt==0);
return
I don't particularly understand the code well. Any ideas how to fix this issue?
Thanks
When generating the code, you can use the -args flag to specify the size, class, and complexity of your input arguments. You can explicitly cast z_in as a complex number to ensure that the code generation succeeds.
codegen fxpt_sgn_saturate -args {complex(z_in), double(Nb_out)}
Alternately, if you're calling this function from within many other functions and are only calling codegen on the top-level function, then you'll want to explicitly cast the input as a complex datatype within your code prior to calling the function so that codegen can appropriately determine the complexity of the input.
function a(thing)
fxpt_sgn_saturate(complex(thing), 10);
end

Including time as an explicit variable in constraint in a Pyomo Model

I am using PyOMO to model a semi-batch reaction.
Consider an ODE system that describes a semi-batch reactor where one of the reactants is fed at a given volume flow for t1 units of time, the reaction goes on until t end, and obviously t1 < t end.
To specify the stop in the flow, I can either use a conditional rule (assume t1 = 3.5*60):
def _vol_flow_in_schedule(mod,t):
if t<=3.5*60:
return mod.vol_flow_in[t] == (12.3/1000)/(3.5*60)
else:
return mod.vol_flow_in[t] == 0
m1.vol_flow_in_schedule = Constraint(m1.time,rule=_vol_flow_in_schedule)
which will create a discontinuity (and then my model does not converge). What I want to do is use a sigmoidal function that will transition the flow to zero without a discontinuity.
To implement the sigmoidal though I need to refer to the time variable itself.
The below MATLAB code gives me the result I want:
t=[0:1:500];
acc=2; %Acceleration parameter, higher values yields sharper change.
time_of_step=3.5*60;
init_value = (12.3/1000)/(3.5*60);
end_value = 0;
sigmoidal=(init_value+(end_value-init_value)/2)...
+((end_value-init_value)/2)*atan((t-time_of_step)*acc)/atan(max(t));
This implementation however needs the time variable explicitly in the function. How can I access the time variable inside the PyOMO rule? I tried the below, but I get an " Cannot treat the scalar component 't_of_step' as an array" error:
m1.init_value = Param(initialize = (12.3/1000)/(3.5*60))
m1.end_value = Param(initialize = 0)
m1.t_of_step = Param(initialize = 210)
m1.acc = Param(initialize = 5)
.
.
def _vol_flow_sigmoidal (mod,t):
return mod.vol_flow_in[t] == (mod.init_value+(mod.end_value-mod.init_value)/2)+((mod.end_value-mod.init_value)/2)*atan((t-mod.t_of_step)*mod.acc)/atan(1500)
m1.vol_flow_sigmoidal = Constraint(m1.time,rule=_vol_flow_sigmoidal)
Hopefully I've described clearlyt what I am after. Any hints are most welcome,
Thanks!
Sal
How are you declaring the m1.time index?
My guess is that you are using a NumPy array to initialize the m1.time index. There is a known problem in Pyomo (see Issue #31) where the NumPy operator overloading and the Pyomo operator overloading end up fighting with each other (basically, NumPy gets fooled into thinking Pyomo scalars are actually indexed and attempts to treat them like arrays).
I was able to reproduce the error with the following complete example:
# pyomo 4.4.1
from pyomo.environ import *
import numpy as np
m1 = ConcreteModel()
m1.time = Set(initialize=np.array([0,100,200,300,400,500]))
m1.vol_flow_in = Var(m1.time)
m1.init_value = Param(initialize = (12.3/1000)/(3.5*60))
m1.end_value = Param(initialize = 0)
m1.t_of_step = Param(initialize = 210)
m1.acc = Param(initialize = 5)
def _vol_flow_sigmoidal (mod,t):
return mod.vol_flow_in[t] == (mod.init_value+(mod.end_value-mod.init_value)/2)\
+((mod.end_value-mod.init_value)/2)*atan((t-mod.t_of_step)*mod.acc)/atan(1500)
m1.vol_flow_sigmoidal = Constraint(m1.time,rule=_vol_flow_sigmoidal)
There are two alternatives that do work, both based on avoiding using NumPy arrays to initialize Pyomo Sets. You can either completely avoid Numpy:
m1.time = Set(initialize=[0,100,200,300,400,500])
or explicitly cast the NumPy array to a list:
timeArray = np.array([0,100,200,300,400,500])
m1.time = Set(initialize=timeArray.tolist())
Finally, for completeness, two other notes:
This also applies to initializing ContinuousSet objects in pyomo.dae
You will see the same behavior even if you avoid the explicit Pyomo Set declaration. That is, the following will also generate the error:
m1.time = np.array([0,100,200,300,400,500])
# ...
m1.vol_flow_sigmoidal = Constraint(m1.time,rule=_vol_flow_sigmoidal)
This is because Pyomo will quietly create the Set object for you behind the scenes as m1.vol_flow_sibmodial_index and then use that Set to index the Constraint.

if-condition on array of booleans in Modelica

I'm sorry if this is a 'read the manual' question (I did but can't find an answer).
I have an array of Booleans and I want to test if any of them is true.
model TestArray
(...)
Boolean[:] booleanArray;
Real y;
equation
y = if [if any element in booleanArray is true] then ... else ...;
end TestArray;
How can I do this?
Thanks,
Roel
There are functions like the ones you are requesting in Modelica.Math.BooleanVectors.
Here you'll find allTrue(Boolean b[:]), anyTrue(Boolean b[:]) and oneTrue(Boolean b[:]).
This is an interesting question. Frankly, I'm not aware of any built-in capabilities for doing this (although the need for such capabilities is certainly valid).
What we've frequently done in the past is to write utility functions called "any" and "all", that look like this (untested, but you get the idea):
function any
input Boolean vals[:];
output Boolean result;
algorithm
result := max({if i==true then 1 else 0 for i in vals})==1;
end any;
function all
input Boolean vals[:];
output Boolean result;
algorithm
result := min({if i==true then 1 else 0 for i in vals})==1;
end all;
This is similar to what you did but using array comprehensions and then encapsulating that in functions. This allows you to write code like:
if any(conditions) then ... else ...;
Ideally, these functions could be added to the built-in set of "reduction operators" (like min and max), but the language group tends to be somewhat conservative about introducing such operators because they pollute the namespace and create potential collisions with existing code.
Note that things get a bit tricky when using when clauses. With when clauses, there is a vector construction, e.g.
when {cond1, cond2, cond3} then
...
end when;
Which has very useful semantics, but is not 100% analogous to either "any" or "all" as written above. So if you intend to use a vector of conditions in a when clause, then read up on how this is handled (in the specification) or ask a follow-up question on that and I can elaborate more (it is somewhat beyond the scope of this question).
Section 10.3.4 of Modelica Specification Version 4.3 allows Boolean arrays v as arguments of min(v) and max(v).
If all components of v are true then min(v) gives true, false otherwise.
If all components of v are false then max(v) gives false, true otherwise.
Example model:
model Test
Boolean anyFalseGivesFalse = min( { true, false } );
Boolean allTrueGivesTrue = min( { true, true } );
Boolean allFalseGivesFalse = max( { false, false } );
Boolean anyTrueGivesTrue = max( { false, true } );
end Test;
Now I found a workaround, but it must be possible to do it much nicer:
model TestArray
(...)
Boolean[:] booleanArray;
Real y;
Real[:] test;
equation
for i in 1:size(booleanArray):
test[i] = if booleanArray[i] then 1 else 0;
end for;
y = if sum(test) > 0 then ... else ...;
end TestArray;
You could use Modelica.Blocks.Math.BooleanToInteger to convert your Boolean-array to an Integer-array with which you can calculate ...