Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I'm working on a project for school that basically involves iteratively solving a cubic equation. I'm using MATLAB for it, but I've never really done much with MATLAB so I'm having some trouble with the logic of it.
Here's my code:
% Redlich/Kwong EOS
sigma = 1;
epsilon = 0;
omega = 0.08664;
psi = 0.42748;
beta = #(Psat_RK) omega*PsatRK/Pc/Tr(1); % Pc is in bar, may need a unit conversion later
alpha = (Tr(1))^(-1/2);
q = psi*alpha/omega/Tr(1);
A = #(beta) (sigma + epsilon - 1)*beta - 1;
B = #(beta) (sigma*epsilon - sigma - epsilon)*(beta^2) + (q - sigma - epsilon)*beta;
C = #(beta) -(sigma*epsilon*(1+beta) + q)*(beta^2);
Q = #(A,B) ((A^2) - 3*B)/9;
R = #(A,B,C) (2*(A^3) - 9*A*B + 27*C)/54;
M = #(R,Q) R^2 - Q^3;
if M > 0
Z_single = ((-R+(M^0.5))^(1/3)) + ((-R-(M^0.5))^(1/3)) - (A/3);
I = (1/(sigma-epsilon))*log((Z_single+sigma*beta)/(Z_single+epsilon*beta));
end
if M < 0
theta = acos(R/(Q^(1/3)));
Z(1) = -2*(Q^0.5)*(cos(theta/3)) - (A/3);
Z(2) = -2*(Q^0.5)*(cos((theta + 2*pi)/3)) - (A/3);
Z(3) = -2*(Q^0.5)*(cos((theta - 2*pi)/3)) - (A/3);
Z_liquid = min(Z)
Z_vapor = max(Z)
I_liquid = (1/(sigma-epsilon))*log((Z_liquid+sigma*beta)/(Z_liquid+epsilon*beta));
I_vapor = (1/(sigma-epsilon))*log((Z_vapor+sigma*beta)/(Z_vapor+epsilon*beta));
end
ln_phi_liquid = Z_liquid - 1 - log(Z_liquid - beta) - q*I_liquid;
ln_phi_vapor = Z_vapor - 1 - log(Z_vapor - beta) - q*I_vapor;
objfun = (ln_phi_liquid - ln_phi_vapor);
Psat_RK_solved = fsolve(objfun,10);
Basically, I'm trying to iterate on the value of Psat_RK until the objfun equals 0. I can post more details of the math if needed, but I figured this would be enough to get started. Thanks.
Edit: Sorry, forgot to actually mention the problem.
Here's the error I'm getting.
Undefined operator '>' for input arguments of type 'function_handle'.
Error in Proj2 (line 73)
if M > 0
I can't figure out how to establish in this line that M is being calculated off an anonymous function.
EDIT:
sigma = 1;
epsilon = 0;
omega = 0.08664;
psi = 0.42748;
beta = #(Psat_RK) omega*PsatRK/Pc/Tr(1); % Pc is in bar, may need a unit conversion later
alpha = (Tr(1))^(-1/2);
q = psi*alpha/omega/Tr(1);
A = #(Psat_RK) (sigma + epsilon - 1)*beta(Psat_RK) - 1;
B = #(Psat_RK) (sigma*epsilon - sigma - epsilon)*(beta(Psat_RK)^2) + (q - sigma - epsilon)*beta(Psat_RK);
C = #(Psat_RK) -(sigma*epsilon*(1+beta(Psat_RK)) + q)*(beta(Psat_RK)^2);
Q = #(Psat_RK) ((A(Psat_RK)^2) - 3*B(Psat_RK))/9;
R = #(Psat_RK) (2*(A(Psat_RK)^3) - 9*A(Psat_RK)*B(Psat_RK) + 27*C(Psat_RK))/54;
M = #(Psat_RK) R(Psat_RK)^2 - Q(Psat_RK)^3;
if M(Psat_RK) > 0
Z_single = ((-R+(M^0.5))^(1/3)) + ((-R-(M^0.5))^(1/3)) - (A/3);
I = (1/(sigma-epsilon))*log((Z_single+sigma*beta)/(Z_single+epsilon*beta));
end
if M(Psat_RK) < 0
theta = acos(R/(Q^(1/3)));
Z(1) = -2*(Q^0.5)*(cos(theta/3)) - (A/3);
Z(2) = -2*(Q^0.5)*(cos((theta + 2*pi)/3)) - (A/3);
Z(3) = -2*(Q^0.5)*(cos((theta - 2*pi)/3)) - (A/3);
Z_liquid = min(Z)
Z_vapor = max(Z)
I_liquid = (1/(sigma-epsilon))*log((Z_liquid+sigma*beta)/(Z_liquid+epsilon*beta));
I_vapor = (1/(sigma-epsilon))*log((Z_vapor+sigma*beta)/(Z_vapor+epsilon*beta));
end
ln_phi_liquid = Z_liquid - 1 - log(Z_liquid - beta) - q*I_liquid;
ln_phi_vapor = Z_vapor - 1 - log(Z_vapor - beta) - q*I_vapor;
objfun = (ln_phi_liquid - ln_phi_vapor);
Psat_RK_solved = fsolve(objfun,10);
I know the code needs some work further down, but the code below shouldn't affect why it hangs at the first if statement, right? The error is:
Undefined function or variable 'Psat_RK'.
Error in Proj2 (line 122)
if M(Psat_RK) > 0
It looks like you are trying to use anonymous functions incorrectly.
If we take a look at one of them:
Q = #(A,B) ((A^2) - 3*B)/9;
To MATLAB, this is the equivalent of this function:
function C = Q(A, B)
C = ((A^2) - 3*B) / 9;
end
Q is the name of the function and doesn't represent a value. If, however, you pass Q the two arguments that it needs (A and B), then it will yield a value.
Obviously, you would want to call this in the following way:
value = Q(a,b);
If you look at your own code, you try to use Q directly as if it were a value rather than a function handle.
Z(1) = -2*(Q^0.5)*(cos(theta/3)) - (A/3);
If we break down this single line a little more, we realize that A (one of the inputs to Q) is also an anonymous function. Same actually goes for B.
Then further down the rabbit hole, A and B depend upon the output of the anonymous function beta which is finally defined at the top.
beta = #(Psat_RK) omega*PsatRK/Pc/Tr(1);
So assuming we have a value for Psat_RK, this whole chain would look like this.
betaValue = beta(Psat_RK);
aValue = A(betaValue);
bValue = B(betaValue);
qValue = A(aValue, bValue);
Now you can use qValue as a value and the statement above would become
Z(1) = -2 * (qValue ^ 0.5) * (cos(theta / 3)) - (aValue / 3);
If you wanted to simplify this, you could redefine Q to be:
Q = #(Psat_RK)(A(beta(Psat_RK))^2 - 3 * B(beta(Psat_RK))) / 9;
This applies to all anonymous functions you have defined (including M which is giving you your first error).
Summary
Anonymous functions are useful for a number of things and functional programmers love them. For your case, I would probably recommend that you just write a simple function that is a function of Psat_RK and create a single anonymous function for that and pass it to fsolve.
fsolve(#objectiveFUnction, x0);
function value = objectiveFunction(Psat_RK)
% Do all your calculations here to get objfun given Psat_RK
% No anonymous functions needed here!
end
Addendum
If we wanted to convert all of your anonymous functions to be a function of Psat_RK they would look like this.
A = #(Psat_RK) (sigma + epsilon - 1) * beta(Psat_RK) - 1;
B = #(Psat_RK) (sigma * epsilon - sigma - epsilon)*(beta(Psat_RK)^2) + (q - sigma - epsilon)*beta(Psat_RK);
C = #(Psat_RK) -(sigma*epsilon*(1+beta(Psat_RK)) + q)*(beta(Psat_RK)^2);
Q = #(Psat_RK) ((A(Psat_RK)^2) - 3*B(Psat_RK))/9;
R = #(Psat_RK) (2*(A(Psat_RK)^3) - 9*A(Psat_RK)*B(Psat_RK) + 27*C(Psat_RK))/54;
M = #(Psat_RK) R(Psat_RK)^2 - Q(Psat_RK)^3;
Example
Here is how I would write this as a separate function without all of those anonymous functions.
objectiveFunction.m
function value = objectiveFunction(psat)
% Redlich/Kwong EOS
sigma = 1;
epsilon = 0;
omega = 0.08664;
psi = 0.42748;
% Pc is in bar, may need a unit conversion later
beta = omega * psat / Pc / Tr(1); % NOT SURE WHAT Tr or Pc ARE
alpha = (Tr(1))^(-1/2);
q = psi*alpha/omega/Tr(1);
A = (sigma + epsilon - 1)*beta - 1;
B = (sigma*epsilon - sigma - epsilon)*(beta^2) + (q - sigma - epsilon)*beta;
C = beta -(sigma*epsilon*(1+beta) + q)*(beta^2);
Q = ((A^2) - 3*B)/9;
R = (2*(A^3) - 9*A*B + 27*C)/54;
M = R^2 - Q^3;
if M > 0
Z_single = ((-R+(M^0.5))^(1/3)) + ((-R-(M^0.5))^(1/3)) - (A/3);
I = (1/(sigma-epsilon))*log((Z_single+sigma*beta)/(Z_single+epsilon*beta));
end
if M < 0
theta = acos(R/(Q^(1/3)));
Z(1) = -2*(Q^0.5)*(cos(theta/3)) - (A/3);
Z(2) = -2*(Q^0.5)*(cos((theta + 2*pi)/3)) - (A/3);
Z(3) = -2*(Q^0.5)*(cos((theta - 2*pi)/3)) - (A/3);
Z_liquid = min(Z);
Z_vapor = max(Z);
I_liquid = (1/(sigma-epsilon))*log((Z_liquid+sigma*beta)/(Z_liquid+epsilon*beta));
I_vapor = (1/(sigma-epsilon))*log((Z_vapor+sigma*beta)/(Z_vapor+epsilon*beta));
end
ln_phi_liquid = Z_liquid - 1 - log(Z_liquid - beta) - q*I_liquid;
ln_phi_vapor = Z_vapor - 1 - log(Z_vapor - beta) - q*I_vapor;
value = (ln_phi_liquid - ln_phi_vapor);
end
Then from the MATLAB command window, you could type the following to get your solution.
Psat_RK_solved = fsolve(#objectiveFunction, 10);
This way, the only anonymous function is the one that you use to point fsolve to your actual objective function.
Related
I have created a program in Matlab to try to find the root of f(x) = exp(2x) + 3x - 4 (the function "fopg1" in my code). My code is as follows:
format long
tic;
for dum=1:1000;
x(1) = 0.5;
x(2) = 0.4;
err_tol = 1e-8;
iteration(1) = 1;
n = 3;
while err_estimate > err_tol
iteration(n) = n;
x(n) = x(n-1) - fopg1(x(n-2)) * ((x(n-1) - x(n-2)) / (fopg1(x(n-1)) - fopg1(x(n-2))));
err_estimate(n) = abs(x(n) - x(n-1));
n = n + 1;
end
%end
time = toc;
avgtime = time/1000;
A = [iteration' x' err_estimate' tbd'];
f = '%2i %13.9f %13.9f %7.3f'; compose(f,A)
Unfortunately this does not converge. I feel like it should. Is there a flaw in my program or does it in fact not converge? Thanks in advance.
Maarten
I answered a very similar question here a few days ago. Using the same code, without an iterations limit and with an increased tolerance (1e-8 as per your example), I detect the expected convergence of exp(2x) + 3x - 4 using the secant method:
clear();
clc();
com = Inf;
i = 2;
err_tol = 1e-8;
f = #(x) exp(2*x) + 3*x - 4;
x(1) = 0.5;
x(2) = 0.4;
while (abs(com) > err_tol)
com = f(x(i)) * (x(i) - x(i-1)) / (f(x(i)) - f(x(i-1)));
x(i+1)= x(i) - com;
i = i + 1;
n = n - 1;
end
display(['Root X = ' num2str(x(end))]);
The message I receive is: Root X = 0.47369. It shouldn't be hard for you to implement your additional data within this code.
I am using following objective function for optimization:
function Length_Sum = objective_function( l1,l2,l3 )
Length_Sum = l1 + l2 + l3;
end
With constraints function given below, the constrint function uses another function for calculating values of thetas,
function [c, ceq] = simple_constraint(l1,l2,l3)
c(1) = l3^2 + 200*l3*cos(30) + 10000 - (l1 + l2)^2;
c(2) = (100- l3*cos(30))^2 + (100*sin(30))^2 - (l1-l2)^2;
thetas = inverse_kinematics(l1,l2,l3);
c(3) = thetas(4,1) - 160;
c(4) = thetas(4,2) - 160;
c(5) = thetas(4,3) - 160;
c(6) = 20 - thetas(4,1);
c(7) = 20 - thetas(4,2);
c(8) = 20 - thetas(4,3);
c(9) = thetas(5,1) - 340;
c(10) = thetas(5,2) - 340;
c(11) = thetas(5,3) - 340;
c(12) = 200 - thetas(5,1);
c(13) = 200 - thetas(5,2);
c(14) = 200 - thetas(5,3);
c(15) = thetas(6,1) - 340;
c(16) = thetas(6,2) - 340;
c(17) = thetas(6,3) - 340;
c(18) = 200 - thetas(6,1);
c(19) = 200 - thetas(6,2);
c(20) = 200 - thetas(6,3);
ceq = [];
end
Function called by constraint function is given below,
function thetas = inverse_kinematics(l1,l2,l3)
x = 100;
y = 0;
phi = 210*pi/180:60*pi/180:330*pi/180;
x1 = x - (l3*cos(phi));
y1 = y - (l3*sin(phi));
a = sqrt(x1.^2 + y1.^2);
y2 = -y1./a;
x2 = -x1./a;
gamma = atan2(y2,x2);
c = (- x1.^2 - y1.^2 - l1^2 + l2^2)./(2*l1*a);
d = acos(c);
theta1 = gamma + d;
if theta1 < 0
theta1 = theta1 + 2*pi;
end
theta4 = gamma - d;
if theta4 < 0
theta4 = theta4 + 2*pi;
end
e = (y1 - l1*sin(theta1))/l2;
f = (x1 - l1*cos(theta1))/l2;
theta2 = atan2(e,f) - theta1;
if theta2 < 0
theta2 = theta2 + 2*pi;
end
g = (y1 - l1*sin(theta4))/l2;
h = (x1 - l1*cos(theta4))/l2;
theta5 = atan2(g,h) - theta4;
if theta5 < 0
theta5 = theta5 + 2*pi;
end
theta3 = (phi)- (theta1 + theta2);
if theta3 < 0
theta3 = theta3 + 2*pi;
end
theta6 = (phi)- (theta4 + theta5);
if theta6 < 0
theta6 = theta6 + 2*pi;
end
thetas = [theta1;theta2;theta3;theta4;theta5;theta6].*180/pi;
end
After running this code using ga toolbox, with lower bounds [20 20 20] and upper bounds [100 100 100] and rest parameters set to default, I am getting "Error in running optimization. Not enough input arguments" error. Can someone help?
ga accepts constraint function with input in form of one vector with number of elements corresponding to number of constrainded variables. You should change
function [c, ceq] = simple_constraint(l1,l2,l3)
to
function [c, ceq] = simple_constraint(input)
l1 = input(1);
l2 = input(2);
l3 = input(3);
Next time, I suggest you try File->Generate Code... option from the mentioned toolbox. Then you can debug more easily from Matlab window.
There is also another problem in your program. Try running inverse_kinematics(20,20,20). It fails on line 29, but I won't go into details here, because this is not part of the question.
I am trying to find out the root of an equation constraint using Newton's Method (open to any other method). The equation constraint is dependent on the roots of a quadratic equation which has one unknown term. The equation descriptions (quadratic and constraint) are shown below. The roots of the quadratic equation are assumed to be p1 and p2.
x^2 + x*(c1*unknown/2 + C1*c2)/(c1*c2*c3*unknown/2) + 1/(c1*c2*c3*u/2) = 0
with constraint
(1/(p2-p1))*(exp(-0.3*p2) - 1)*(c1*c2 - p2) - (exp(-0.3*p1) - 1)*(c1*c2 - p1) - 0.04 = 0
I am wondering if there are any other approximation methods to solve this problem if Newton's Method is not going to do so.
Matlab Code
p0=10*10^6;
Cc=0.65*10^-6;Rp=100*10^3;Cp=55*10^-9; z1=1/(Cp*Rp);
N = 100;error = 0.02;
syms 'x'
a = 1;
b = ((Cc*(x/2+Rp))/(Cc*Cp*Rp*x/2));
c = 1/(Cc*Cp*Rp*x/2);
poles1 = (-b + sqrt(b^2 - 4*a*c))/(2*a);
poles2 = (-b - sqrt(b^2 - 4*a*c))/(2*a);
p1_subterm = (exp(-0.3*poles1) - 1)*(z1 - poles1);
p2_subterm = (exp(-0.3*poles2) - 1)*(z1- poles2);
f(x) = (1/(poles2 - poles1))*p2_subterm - p2_subterm - 0.04;
df = diff(f);
while i <= N
p = p0-(f(p0)/df(p0))
if (abs(p - p0)/abs(p)) < error
fprintf('Solution is %f \n', double(p))
return
end
i = i + 1;
p0 = p;
end
fprintf('Solution did not converge within %d iterations \n', N)
I don't think your code for f(x) corresponds to the equation listed above: In the MATLAB code 1/(poles2-poles1) multiplies both the exp((...)poles2) and the exp((...)poles1) terms and you have 1/(c1*c2) - poles1 instead of c1*c2 - poles1.
I have not checked through the poles1 and poles2 terms as I find the 10 sets of parenthesis intimidating. It would suggest making your code more readable by breaking up each of those equations in to a number of shorter, clearer lines.
Noting that we can multiply the quadratic by c1*c2*c3*unknown,
(c1*c2*c3*unknown)*x^2 + x*2*(c1*unknown/2 + C1*c2) + 2 = 0
then you can write the poles and f code as
% Here unknown is x
syms 'x'
a = c1*c2*c3*x;
b = c1*x + 2*c1*c2;
c = 2;
poles1 = (-b + sqrt(b^2 - 4*a*c))/(2*a);
poles2 = (-b - sqrt(b^2 - 4*a*c))/(2*a);
p1_subterm = (exp(-0.3*poles1) - 1)*(c1*c2 - poles1);
p2_subterm = (exp(-0.3*poles2) - 1)*(c1*c2 - poles2);
f(x) = (1/(poles2 - poles1))*p2_subterm - p2_subterm - 0.04;
I have a function that seems to be working correctly when I give it single inputs to test, but I need it to work on a lot of different variables. Right now at the bottom it's evaluating the function with the initial guess of 25, and the index of 50. In playing around with it, I noticed that the index is actually chanding as fsolve runs, so I figure that I have an incomplete understanding of how fsolve is actually running here, but I can't fix it. My end goal is that I'll be able to cycle the index from 1 to 54 in a for loop, and inside that loop, the initial guess will change based on an algorith I already have running. So pretty much I just need to sort out this function input problem.
Here's my code:
function [objfun] = RK_solver( RK_solver_input )
% Redlich/Kwong EOS
index = RK_solver_input(2)
sigma = 1;
epsilon = 0;
omega = 0.08664;
psi = 0.42748;
beta = omega * RK_solver_input(1) / (Input_Data(2)/1.01325) / Tr(index);
alpha = (Tr(index))^(-1/2);
q = psi*alpha/omega/Tr(index);
A = (sigma + epsilon - 1)*beta - 1;
B = (sigma*epsilon - sigma - epsilon)*(beta^2) + (q - sigma - epsilon)*beta;
C = -(sigma*epsilon*(1+beta) + q)*(beta^2);
Q = ((A^2) - 3*B)/9;
R = (2*(A^3) - 9*A*B + 27*C)/54;
M = R^2 - Q^3;
if M < 0
theta = acos(R/(sqrt(Q*Q*Q)));
Z(1) = -2*(Q^0.5)*(cos(theta/3)) - (A/3);
Z(2) = -2*(Q^0.5)*(cos((theta + 2*pi)/3)) - (A/3);
Z(3) = -2*(Q^0.5)*(cos((theta - 2*pi)/3)) - (A/3);
Z_liquid = min(Z);
Z_vapor = max(Z);
I_liquid = (1/(sigma-epsilon))*log((Z_liquid+sigma*beta)/(Z_liquid+epsilon*beta));
I_vapor = (1/(sigma-epsilon))*log((Z_vapor+sigma*beta)/(Z_vapor+epsilon*beta));
ln_phi_liquid = Z_liquid - 1 - log(Z_liquid - beta) - q*I_liquid;
ln_phi_vapor = Z_vapor - 1 - log(Z_vapor - beta) - q*I_vapor;
objfun = (ln_phi_liquid - ln_phi_vapor);
end
end
RK_solver_guess = [25 50];
Psat_RK_solved = fsolve(#RK_solver, RK_solver_guess)
The issue that you are seeing is because fsolve assumes that it can change any of the parameters specified as the second input to fsolve. In your example you pass the index as part of this initial guess, so fsolve includes the index in the optimization. As a result, fsolve is attempting to modify your real parameter (as you intended) as well as the index in an attempt to minimize the function.
Instead, you will want to specify the index as an input to just the function but not as a parameter for fsolve to use in the minimization.
You can accomplish this by modifying your anonymous function
% Can be whatever you want them to be
guesses = rand(54, 1);
for index = 1:numel(guesses)
solution(index) = fsolve(#(guess)RK_solver(guess, index), guesses(index));
end
If you break down that anonymous function, you'll see that fsolve will supplies the guess variable and then I will add the index variable myself to the function call to RK_solver.
Obviously, you would also need to modify your function definition slightly to handle the parameters and index as two parameters rather than a vector.
function objfun = RK_solver(RK_solver_input, index)
My question today is related to this previous question. I am following this research paper. I am trying to duplicate figure 8 located on page 20. I have a screenshot:
I'm confused on how to plot the left figure this in MATLAB because now a instead of having time varying we have the treatment varying. Here's what I have from the previous question:
function dX = CompetitionModel(~,X)
bs = 8e-3;
bl = 4e-3;
bh = 6.4e-3;
N = sum(X);
K = 1e8;
m1 = 2e-5;
m2 = 9e-9;
p = 5e-13;
I = 1e-3;
T = 1e-3; % Treatment
a = 0;
dX = [X(1) * (bs * (1 - N/K) - I - T - m1) - p * X(1) * (X(2) + X(3));
X(2) * (bl * (1 - N/K) - I - a*T - m2) + m1 * X(1) + p * X(2) * (X(1) - X(3));
X(3) * (bh * (1 - N/K) - I - a*T) + m2 * X(2) + p * X(3) * (X(1) + X(2))];
end
To plot my equations in the previous question, I typed the following in the command window:
>> [t,Y] = ode45(#CompetitionModel, [0 4.5e4], [1e4 0 0]);
>> plot(t,X(:,1), t,X(:,2), t,X(:,3))
In my function file, I have Treatment already defined. I'm guessing that it shouldn't be anymore. So what can I do so that I have Treatment varying instead of time? I hope my question makes sense.
You still solve the equation in regards to the time - but solely plot the value at the time t = 1 month.
To vary the treatment you need an additional loop around the ode45 call and pass the current treatment-value to the function dX
for treatment = 10^-4:10^-5:10^-3
[t,Y] = ode45(#CompetitionModel, [0 4.5e4], [1e4 0 0], [] , treatment);
plot(treatment,Y(end,1), 'x')
plot(treatment,Y(end,2), 'kx')
plot(treatment,Y(end,3), 'rx')
hold on
end
the function dX now has to be changed to accept the treatment input:
function dX = CompetitionModel(~,X, T)
Finally, comment your old treatment assignment in the function dX: %T = 1e-3; % Treatment