Removing if statements when obtaining data from a table - matlab

I have the following table
nc a b
0.9 <= nc 0.33 -0.45
0.5 <= nc < 0.9 0.95 -0.75
0.1 <= nc < 0.5 2.2 -0.97
nc < 0.1 1.18 -0.77
which says that if the observed cloud cover if between the ranges shown then a and b are given as the values expressed in the table. I have written some code in matlab that should return these values given a vector of nc values:
nc = [0.1, 0.6, 0.5 ,0.2, 0.9];
a = nan(length(nc),1); % pre-allocate arrays
b = nan(length(nc),1);
for i = 1:length(nc)
if nc(i) >= 0.9;
ai = 0.33;
bi = -0.45;
elseif nc(i) >= 0.5 & nc(i) < 0.9
ai = 0.95;
bi = -0.75;
elseif nc(i) >= 0.1 & nc(i) < 0.5
ai = 2.2;
bi = -0.97;
elseif nc(i) < 0.1
ai = 1.18;
bi = -0.77;
end
a(i) = ai;
b(i) = bi;
end
However, this seems like a very long way of doing this. In addition, this code will eventually be fed into a number of other functions and I'm a bit worried that having so many if statements and a for loop in general will slow things down. Could anyone comment on this or suggest a faster way (if useful) of doing this?

loops in matlab are slow. You can improve your solution vectorizing the operation:
nc = [0.1, 0.6, 0.5 ,0.2, 0.9];
a = 2.2 * ones(length(nc),1); % by default in the 0.1-0.5 range
b = -0.97 * ones(length(nc),1);
f = find(nc >= 0.9);
a(f) = 0.33; b(f) = -0.45;
f = find(nc >= 0.5 & nc < 0.9);
a(f) = 0.95; b(f) = -0.75;
f = find(nc < 0.1);
a(f) = 1.18; b(f) = -0.77;
find will list the indices of the element that match the criteria, so you can modify the elements in the other vectors.

Or you can use logical indexing, by-passing the need to use the find function:
nc = [0.1, 0.6, 0.5 ,0.2, 0.9];
a = 2.2 * ones(length(nc),1); % by default in the 0.1-0.5 range
b = -0.97 * ones(length(nc),1);
a(nc >= 0.9) = 0.33; b(nc >= 0.9) = -0.45;
a(nc >= 0.5 & nc < 0.9) = 0.95; b(nc >= 0.5 & nc < 0.9) = -0.75;
a(nc < 0.1) = 1.18; b(nc < 0.1) = -0.77;

Related

math.acos out of domain error when combined with backlash element

i've run into a problem with out of domain error involving modelica trigonometric functions and i just dont know what i could do.
My model is that of a nonlinear rotational spring (torque dependant on geometric relations) + a hard stop (using standard backlash element).
Model schematic with elastogap - doesnt work
The error i get in OpenModelica is:
acos(variable) outside domain -1.0 <= -1 <= +1.0
That means the error appears to be in my custom spring model.
Now, the ordinary solution i thought should work is to limit the variable so acos stays in domain using if-conditions. I also followed the advice given here about the noEvent() operator. This did not work however. I even went overboard with the "safekeeping", to no avail.
For completions sake, this is the geometric relation of the spring. I'm positive that it is correct.
I use an input ramp from phi= -2° to 108°. The elastogap allows force-free movement from phi = 0° to 106°.
Without the backlash element my spring works as i expect . No issues with out of domain. Together with this backlash i get the domain issue, even though i added "domain-limits" before every trigonometric function in my custom spring model.
Does anyone have an idea what the issue could be/ what i could try?
I noticed that if i change the range of the backlash to have different limits (for example b=105.9*pi/180 ; phi_rel0=-52.95*pi/180) it also works without domain error, but i would like to get to the bottom of this issue.
The model (with the error):
package test
model LD_Abwickel_ZF_VAL
Modelica.Blocks.Sources.Ramp ramp2(duration = 1.24, height = 122, offset = -2);
Modelica.Mechanics.Rotational.Components.ElastoBacklash2 elastoBacklash(b = 1.85005, c = 1e4, d = 1e2, phi_rel0 = -0.925025);
Modelica.Mechanics.Rotational.Components.Fixed fixed2;
Modelica.Mechanics.Rotational.Sources.Position position1(exact = true);
Modelica.Blocks.Math.UnitConversions.From_deg from_deg1;
test.LD_Pendelarm_ZugfederF2 lD_Pendelarm_ZugfederF21;
equation
connect(position1.flange, lD_Pendelarm_ZugfederF21.flange);
connect(elastoBacklash.flange_a, lD_Pendelarm_ZugfederF21.flange);
connect(ramp2.y, from_deg1.u);
connect(from_deg1.y, position1.phi_ref);
connect(elastoBacklash.flange_b, fixed2.flange);
end LD_Abwickel_ZF_VAL;
model LD_Pendelarm_ZugfederF2
Modelica.Mechanics.Rotational.Interfaces.Flange_a flange;
.Modelica.SIunits.Angle phi(displayUnit = "deg");
.Modelica.SIunits.Torque M;
parameter .Modelica.SIunits.Length a(displayUnit = "mm") = 25.49e-3;
parameter .Modelica.SIunits.Length b(displayUnit = "mm") = 23.38e-3;
parameter .Modelica.SIunits.Length d(displayUnit = "mm") = 43.89e-3;
parameter .Modelica.SIunits.Length L0(displayUnit = "mm") = 59.5e-3 "Ungespannte Laenge d. Feder";
parameter .Modelica.SIunits.Length Lk(displayUnit = "mm") = 47.19e-3 "Laenge unbelasteter Federkörper";
parameter .Modelica.SIunits.Force F0 = 4.1 "innere Vorspannkraft Feder";
parameter .Modelica.SIunits.TranslationalSpringConstant R(displayUnit = "N/mm") = 0.868e+3 "Federkonstante";
parameter Integer z = 2 "Anzahl Federn";
.Modelica.SIunits.Length c(displayUnit = "mm");
.Modelica.SIunits.Angle delta(displayUnit = "deg", min = -2 * .Modelica.Constants.pi, max = 2 * .Modelica.Constants.pi, nominal = 0);
.Modelica.SIunits.Angle alpha(displayUnit = "deg", min = -2 * .Modelica.Constants.pi, max = 2 * .Modelica.Constants.pi, nominal = 0.5 * .Modelica.Constants.pi);
.Modelica.SIunits.Angle beta(displayUnit = "deg", min = -4 * .Modelica.Constants.pi, max = 4 * .Modelica.Constants.pi, nominal = .Modelica.Constants.pi);
.Modelica.SIunits.Angle psi(displayUnit = "deg", min = -4 * .Modelica.Constants.pi, max = 4 * .Modelica.Constants.pi, nominal = 0);
.Modelica.SIunits.Length L_gerade(displayUnit = "mm");
.Modelica.SIunits.Length L_bogen(displayUnit = "mm") = 46.1e-3;
.Modelica.SIunits.Length L_c(displayUnit = "mm") "rel. Federlaenge (gesamt)";
.Modelica.SIunits.Force F_c "Summe Federkraft";
Real cosalpha(min = -1, max = 1, nominal = 0);
Real cosdelta(min = -1, max = 1, nominal = 0);
Real cosbeta(min = -1, max = 1, nominal = 0);
equation
flange.phi = phi;
flange.tau = M;
if noEvent(phi <= 0) then
c = a - b;
cosdelta = 1;
delta = 0;
L_gerade = sqrt(c ^ 2 + d ^ 2);
cosalpha = -1;
alpha = .Modelica.Constants.pi;
else
c = sqrt(a ^ 2 + b ^ 2 - 2 * a * b * cos(phi));
cosdelta = (b ^ 2 - a ^ 2 - c ^ 2) / (-2 * a * c);
if noEvent(cosdelta <= 0.999999 and cosdelta >= (-0.999999)) then
cos(delta) = cosdelta;
else
delta = if noEvent(sign(cosdelta) > 0) then 0 else .Modelica.Constants.pi;
end if;
L_gerade = sqrt(c ^ 2 + d ^ 2 - 2 * c * d * cos(delta + .Modelica.Constants.pi * 0.5));
cosalpha = (a ^ 2 - b ^ 2 - c ^ 2) / (-2 * b * c);
if noEvent(cosalpha <= 0.999999 and cosalpha >= (-0.999999)) then
cos(alpha) = cosalpha;
else
alpha = if noEvent(sign(cosalpha) > 0) then 0 else .Modelica.Constants.pi;
end if;
end if;
cosbeta = (d ^ 2 - c ^ 2 - L_gerade ^ 2) / (-2 * c * L_gerade);
if noEvent(cosbeta <= 0.999999 and cosbeta >= (-0.999999)) then
cos(beta) = cosbeta;
else
beta = if noEvent(sign(cosbeta) > 0) then 0 else .Modelica.Constants.pi;
end if;
psi = alpha - beta;
L_c = L_gerade + L_bogen - (L0 - Lk) - Lk;
F_c = z * (R * L_c + F0);
M = F_c * sin(psi);
end LD_Pendelarm_ZugfederF2;
end test;
So of course it works fine on my machine, but from the code I know it is problematic and might fail randomly. The problem is that you have things like cos(beta) = cosbeta which is solved as beta = acos(cosbeta).
But OpenModelica added a bad equation for common subexpression elimination someTmpVar = acos(cosbeta) which always runs regardless of the if-equations you use to guard this. The expansion seems to happen Feels like a bug to report to OpenModelica since it happens during code generation and just produces lD_Pendelarm_ZugfederF21._delta = if noEvent(from_deg1.y <= 0.0) then 0.0 else if noEvent(lD_Pendelarm_ZugfederF21.cosdelta <= 0.999999) and noEvent(lD_Pendelarm_ZugfederF21.cosdelta >= -0.999999) then if noEvent(abs($TMP$VAR$70$0x1$COS - $TMP$VAR$70$0PREX$COS) < abs($TMP$VAR$70$0x2$COS - $TMP$VAR$70$0PREX$COS)) then $TMP$VAR$70$0x1$COS else $TMP$VAR$70$0x2$COS else if noEvent(sign(lD_Pendelarm_ZugfederF21.cosdelta) > 0) then 0.0 else 3.141592653589793, which won't run faster than before and might produce domain errors.
A probable workaround would be to use an algorithm section as it is not manipulated as heavily by OpenModelica.

Changing a parameter in a differential equation according to a condition?

I am trying to change the input wheel velocities of a mobile robot according to a position condition ?
if d > 0 & d < 0.4
p.WR = 0.51;
p.WL = 0.5;
elseif d > 0.4 & d < 0.8
p.WR = 0.5;
p.WL = 0.51;
elseif d == 0.4
p.WR = 0.5;
p.WL = 0.5;
end
function dt = mydglw9(t,c,p)
x = c(1);y = c(2);th = c(3);
dx = (((p.r*p.WL)+(p.r*p.WR))/2) * cos(th);
dy = (((p.r*p.WL)+(p.r*p.WR))/2) * sin(th);
dth= ((p.r*p.WL)-(p.r*p.WR))/p.L;
dt = [dx;dy;dth];
pose = [x y th];
end
however i always get the error [ Reference to non-existent field 'WL' ], is there a way to make the solver check the condition before solving the differential equations ?
You can check whether o not the WL field from the p structure exists with isfield:
isfield(p,'WL')
returns true if the field exists.

Model flow from a pipe

I have been trying to model flow through a pipe that can be partially full, or totally full in modelica and running it in OpenModelica. I have finally reduced the example to essentially just use the area of a circle, and to have no outflow until it is full, then complete outflow. But, I am still getting errors. I have tried it a few different ways. The first way gives an error about solving a nonlinear system once the pipe becomes "filled up". Instead I want it to switch over:
model SimplePipe1
Modelica.SIunits.Area A;
Modelica.SIunits.Mass mass;
Modelica.SIunits.Height level(start = 0.5, fixed = true, min = 0.0, max = 2 * R) "Liquid level (m)";
parameter Modelica.SIunits.Radius R = 1.0 "pipe inner radius (m)";
parameter Real flow_in = 1.0;
Real flow_out;
Modelica.SIunits.Angle phi(start = 3.1, min = 0.0, max = 7.2832) "Angle from center to surface level";
Modelica.SIunits.Area A;
Modelica.SIunits.Mass mass;
Modelica.SIunits.Height level(start = 0.5, fixed = true, min = 0.0, max = 2 * R) "Liquid level (m)";
parameter Modelica.SIunits.Radius R = 1.0 "pipe inner radius (m)";
parameter Real flow_in = 1.0;
Real flow_out;
Modelica.SIunits.Angle phi(start = 3.1, min = 0.0, max = 7.2832) "Angle from center to surface level";
equation
mass = A;
//Assume unit length pipe and unit density
flow_in + flow_out = der(mass);
A = 0.5 * R ^ 2 * (phi - sin(phi));
// phi = if noEvent(level <= 0) then 0 elseif noEvent(level >= 2 * R) then 2 * Modelica.Constants.pi else 2 * acos((R - level) / R);
if noEvent(level <= 0) then
phi = 0;
flow_out = 0;
elseif noEvent(level >= 2 * R) then
phi = 2 * Modelica.Constants.pi;
flow_out = -flow_in;
else
flow_out = 0;
//Partially full pipe has no out outflow
phi = 2 * acos((R - level) / R);
end if;
annotation(Icon, Diagram, experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-06, Interval = 0.02));
end SimplePipe1;
This version seems to give results that are closer to what I want, but it still doesn't work. In this case, the problem is that phi is supposed to be limited to 2*pi. Instead it keeps increasing. Meanwhile, I don't actually see flowmode change. I do see the outflow go negative for a single cycle, then it jumps back to zero. I don't understand what is changing the flowmode back from channel to full, since there is no corresponding "when" to change it back.
model SimplePipe2
type modetype = enumeration(empty, full, channel);
modetype flowmode(start = modetype.channel);
Modelica.SIunits.Area A;
Modelica.SIunits.Mass mass;
Modelica.SIunits.Height level(start = 0.5, fixed = true, min = 0.0, max = 2 * R) "Liquid level (m)";
Modelica.SIunits.Height level_limit;
parameter Modelica.SIunits.Radius R = 1.0 "pipe inner radius (m)";
parameter Real flow_in = 1.0;
Real flow_out;
Modelica.SIunits.Angle phi(start = 3.1, min = 0.0, max = 7.2832) "Angle from center to surface level";
Real flow_out;
initial equation
flowmode = modetype.channel;
equation
mass = A;
//Assume unit length pipe and unit density
flow_in + flow_out = der(mass);
A = 0.5 * R ^ 2 * (phi - sin(phi));
cos(phi / 2) = (R - level) / R;
if flowmode == modetype.empty then
flow_out = 0;
elseif flowmode == modetype.full then
flow_out = -flow_in;
else
flow_out = 0;
//Partially full pipe has no out outflow
end if;
when noEvent(phi >= 2 * Modelica.Constants.pi) then
reinit(flow_out, -flow_in);
reinit(level, 2 * R);
flowmode = modetype.full;
end when;
annotation(Icon, Diagram, experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-06, Interval = 0.02));
end SimplePipe2;
This question that I asked is related to solving the same problem, but doesn't have the issue of a circle/cylinder. And, my second example above is based somewhat on this question.
I am using the newest beta of OpenModelica. My complete model will have other feature that are not included in either of these examples. But, hopefully if I can get this simple version working, I can expand from there.
Your code ends up with a non-linear equation for phi (after level_limit and one flow_out was removed from the model).
0 = 0.5 * R ^ 2.0 * (phi - sin(phi)) - mass
OM solves this without adding constraints for the variable phi. The assertion is instead checked after the solution is found. If you use non-linear solver=kinsol in OpenModelica, the constraints are added to the non-linear equation, but it does not help in this case. I am also a bit unsure if when noEvent() would ever trigger.

Undefined variable in 'if' statement

I'm writing a script for an aerodynamics class and I'm getting the following error:
Undefined function or variable 'dCt_dx'.
Error in Project2_Iteration (line 81)
Ct = trapz(x,dCt_dx)
I'm not sure what the cause is. It's something to do with my if statement. My script is below:
clear all
clc
global dr a n Vinf Vr w rho k x c cl dr B R beta t
%Environmental Parameters
n = 2400; %rpm
Vinf = 154; %KTAS
rho = 0.07647 * (.7429/.9450); %from mattingly for 8kft
a = 1084; %speed of sound, ft/s, 8000 ft
n = n/60; %convert to rps
w = 2*pi*n;
Vinf = (Vinf*6076.12)/3600; %convert from KTAS to ft/s
k = length(c);
dr = R/k; %length of each blade element
for i = 1:k
r(i) = i*dr - (.5*dr); %radius at center of blade element
dA = 2*pi*r*dr; %Planform area of blade element
x(i) = r(i)/R;
if x(i) > .15 && x(i-1) < .15
i_15 = i;
end
if x(i) > .75 && x(i-1) < .75
i_75h = i;
i_75l = i-1;
end
Vr(i) = w*r(i) + Vinf;
%Aerodynamic Parameters
M = Vr(i)/a;
if M > 0.9
M = 0.9;
end
m0 = 0.9*(2*pi/(1-M^2)^0.5); %lift-curve slope (2pi/rad)
%1: Calculate phi
phi = atan(Vinf/(2*pi*n*r(i)));
%2: Choose Vo
Vo = .00175*Vinf;
%3: Calculate Theta
theta = atan((Vinf + Vo)/(2*pi*n*r(i)))-phi;
%4:
if option == 1
%calculate cl(i) from c(i)
sigma = (B*c(i))/(pi*R);
if sigma > 0
cl(i) = (8*x(i)*theta*cos(phi)*tan(phi+theta))/sigma;
else
cl(i) = 0;
end
else %option == 2
%calculate c(i) from cl(i)
if cl(i) ~= 0
sigma = (8*x(i)*theta*cos(phi)*tan(phi+theta))/cl(i);
else
sigma = 0;
end
c(i) = (sigma*pi*R)/B;
if c(i) < 0
c(i) = 0;
end
end
%5: Calculate cd
cd(i) = 0.0090 + 0.0055*(cl(i)-0.1)^2;
%6: calculate alpha
alpha = cl(i)/m0;
%7: calculate beta
beta(i) = phi + alpha + theta;
%8: calculate dCt/dx and dCq/dx
phi0 = phi+theta;
lambda_t = (1/(cos(phi)^2))*(cl(i)*cos(phi0) - cd(i)*sin(phi0));
lambda_q = (1/(cos(phi)^2))*(cl(i)*sin(phi0) + cd(i)*cos(phi0));
if x(i) >= 0.15
dCt_dx(i) = ((pi^3)*(x(i)^2)*sigma*lambda_t)/8; %Roskam eq. 7.47, pg. 280
dCq_dx(i) = ((pi^3)*(x(i)^3)*sigma*lambda_q)/16; %Roskam eq. 7.48, pg 280
else
dCt_dx(i) = 0;
dCq_dx(i) = 0;
end
%calculate Mdd
t(i) = (0.04/(x(i)^1.2))*c(i);
Mdd(i) = 0.94 - (t(i)/c(i)) - cl(i)/10;
end
%9: calculate Ct, Cq, Cd
Ct = trapz(x,dCt_dx)
Cq = trapz(x,dCq_dx)
D = 2*R;
Q=(rho*(n^2)*(D^5)*Cq)
T=(rho*(n^2)*(D^4)*Ct)
When I step through your script, I see that the the entire for i = 1:k loop is skipped because k=0. You set k = length(c), but c was never initialized to a value, so it has length zero.
Because of this, dCt_dx is never given a value--and more importantly the majority of your script is never run.
If you're going to be using MATLAB in the future, I really suggest learning how to do this. It makes it a lot easier to find bugs. Try looking at this video.

MATLAB optimization of the function

I want to optimize this function:
function Y_res = GetY( Y, P )
cnt = length(P);
denom = 0;
for i = 1:cnt
Y(i) = rand;
denom = denom + P(i) / (1 - Y(i));
end
Y_res = 1 - 1 / denom;
Y
Y_res
end
This function receives Y and P. P is a constant array. I need to optimize Y array values. And I trying to do this:
Y = [0.3 0.2 0.1]; % need to be optimized
P = [0.65 0.2 0.15]; % constant array
fhnd_GetY = #(x) GetY(Y, P);
options = optimset('TolX', 1e-3);
optimal_x = fminbnd(fhnd_GetY, 0.2, 0.4, options);
But the result of optimal_x variable does not equals to Y_res, which you can see on the screen during iterations of GetY function. Why? I need to optimze Y_res between 0.2 and 0.4 for example. So as a result I need to receive Y_res between 0.2 and 0.4 (for example) and Y array with values by means of which I can receive Y_res.
Assuming that you only intend to optimize the function GetY and given the fact that you are creating Y inside it and as such don't need to externally input Y into it, see if this modified function is optimized enough for you -
function [Y_res,Y] = GetY( P )
found = false;
while ~found
Y = rand(size(P));
Y_res = 1-1./sum(P./(1-Y));
if Y_res>0.2 & Y_res<0.4
found = true;
end
end
return;
Please note that if you don't need Y as an output, you can use Yres = GetY([0.65 0.2 0.15]) instead, but most probably you would need that (my guess though).
Few sample runs -
>> [Yres,Y] = GetY([0.65 0.2 0.15])
Yres =
0.3711
Y =
0.4243 0.2703 0.1971
>> [Yres,Y] = GetY([0.65 0.2 0.15])
Yres =
0.2723
Y =
0.1897 0.4950 0.1476
>> [Yres,Y] = GetY([0.65 0.2 0.15])
Yres =
0.3437
Y =
0.3624 0.0495 0.4896