How can i use fsolve to plot the solutions to a function? - function

I have a variable of a that is equal to (weight./(1360*pi)).^(1/3), where the weight ranges between 4 and 8kg.
I then have guess of the time taken ,which is 14400 seconds.
The function in question is attached, where infinity is replaced by k=22.
Function in question
This function should be equal to 57/80
r/a can be replaced by 0.464, meaning that the multiplication of the summation can be written as 2/(0.464*pi).
alpha will be equal to 0.7*10^-7
How would i be able to plot the times taken for the masses to cook in hours, for weight in the given range?
I have tried to code this function for a couple of days now but it wont seem to work, due to array size issues and the general function just not working.
Any help would be greatly appreciated :)

First, you need a master equation as a function of weight and t, which you want fsolve to find the zero of. Then for each weight, you can capture it in another function that you then solve for t:
alpha = 0.7e-7;
rbya = 0.464;
k = 1:22;
a = #(weight)(weight./(1360*pi)).^(1/3);
eqn = #(weight,t)2/pi/rbya*sum((-1).^(k-1)./k.*sin(k*pi*rbya).*exp(-1.*k.^2.*pi^2.*alpha.*t./(a(weight).^2)))-57/80;
weights = 4:8;
ts = zeros(size(weights));
for i = 1:numel(weights)
sub_eqn = #(t)eqn(weights(i),t);
ts(i)=fsolve(sub_eqn,14400);
end
plot(weights,ts/(60*60))
xlabel("Weight (kg)")
ylabel("Cooking Time (hrs)")
If you want to solve the entire set of equations at once, then you need to be careful of array sizes (as you have experienced, read more here). k should be a column vector so that sum will sum along each column, and weights should be a row vector so that element-wise operations will repeat the k’s for each weight. You also need your list of initial guesses to be the same size as weights so that fsolve can have a guess for each weight:
alpha = 0.7e-7;
rbya = 0.464;
k = (1:22)';
a = #(weight)(weight./(1360*pi)).^(1/3);
weights = 4:8;
eqn = #(t)2/pi/rbya*sum((-1).^(k-1)./k.*sin(k*pi*rbya).*exp(-1.*k.^2.*pi^2.*alpha.*t./(a(weights).^2)))-57/80;
ts=fsolve(eqn,repmat(14400,size(weights)));
plot(weights,ts/(60*60))
xlabel("Weight (kg)")
ylabel("Cooking Time (hrs)")
Note that you do get slightly different answers with the two methods.

Related

How to calculate values using for-end and if-else in Octave?

I need to calculate variable Nfj in the following equation:
ea_M = (sf/E)*(2*Nfj)^b + ef*(2*Nfj)^c
so that it is equal to 4 different values of ea: 5.0900e-04, 4.3626e-04, 3.6358e-04, and 2.9084e-04. The results should be 4 values of Nfj which should be stored in results. I am rounding because I think that it is not possible to calculated exactly equal values of ea, without rounding the equality would always be false.
I wrote for it this code, but it does not work as expected: the script runs for a very long time without any results. How can I fix it to work correctly?
sf = 882.07;
ef = 0.59;
b = -0.102969;
c = -0.58;
E = 210000;
ea = [5.0900e-04; 4.3626e-04; 3.6358e-04; 2.9084e-04]
for pos = 1:length(ea)
for Nfj = 1e3:10:1e12
ea_M = (sf/E)*(2*Nfj)^b + ef*(2*Nfj)^c;
if round(ea_M * 10^5)/10^5 == round(ea(pos) * 10^5)/10^5;
disp(ea_M)
disp(Nfj)
results(pos) = Nfj;
end
end
end
You are trying every 10th value, from 103 to 1012: there are 1011 values here to try! This is of course a lot. You will be searching for ever, and you might skip the actual value that would make the equality true.
If you are not able to solve the equation manually, you can use a numerical solver. Let's first print the function:
sf = 882.07;
ef = 0.59;
b = -0.102969;
c = -0.58;
E = 210000;
ea_M = #(Nfj) (sf/E)*(2*Nfj).^b + ef*(2*Nfj).^c;
Nfj = logspace(3,12,1000);
plot(Nfj, ea_M(Nfj))
set(gca, 'xscale', 'log')
It looks like this function is nicely monotonic, and the four values you are looking for are in the interval of 103 to 1012 you were searching. To find where it equals one of your values, we can subtract that value and find where it equals zero. You can very quickly narrow down your search if you start at a point where the function is larger than zero, and one where it is smaller than zero. You halve the interval every time, keeping the half of the interval that contains the zero crossing. The fzero function does just this.
ea = [5.0900e-04; 4.3626e-04; 3.6358e-04; 2.9084e-04];
results = zeros(size(ea));
for pos = 1:numel(ea)
results(pos) = fzero(#(Nfj) ea_M(Nfj) - ea(pos), [1e3,1e12]);
end
results
In MATLAB, this code runs in a small fraction of a second and outputs:
results =
1.0e+10 *
0.0429
0.1849
1.0627
9.1919
and ea_M(results) - ea is approximately zero.
A few notes on the code I posted here:
ea_M is defined as an anonymous function. This makes it easier to reuse the expression, rather than writing it over and over again. I replaced ^ with .^ to allow this function to do the computation on an array of Nfj values at once, rather than only single values. This is necessary for the fzero call.
I plotted the function on a logarithmic scale, because this function calls for that. The same is not true for all functions.
I preallocated the results array, you should avoid increasing the size of an array in a loop.

How to solve a second order differential equation on Scilab?

I need to solve this differential equation using Runge-Kytta 4(5) on Scilab:
The initial conditions are above. The interval and the h-step are:
I don't need to implement Runge-Kutta. I just need to solve this and plot the result on the plane:
I tried to follow these instructions on the official "Scilab Help":
https://x-engineer.org/graduate-engineering/programming-languages/scilab/solve-second-order-ordinary-differential-equation-ode-scilab/
The suggested code is:
// Import the diagram and set the ending time
loadScicos();
loadXcosLibs();
importXcosDiagram("SCI/modules/xcos/examples/solvers/ODE_Example.zcos");
scs_m.props.tf = 5000;
// Select the solver Runge-Kutta and set the precision
scs_m.props.tol(6) = 6;
scs_m.props.tol(7) = 10^-2;
// Start the timer, launch the simulation and display time
tic();
try xcos_simulate(scs_m, 4); catch disp(lasterror()); end
t = toc();
disp(t, "Time for Runge-Kutta:");
However, it is not clear for me how I can change this for the specific differential equation that I showed above. I have a very basic knowledge of Scilab.
The final plot should be something like the picture bellow, an ellipse:
Just to provide some mathematical context, this is the differential equation that describes the pendulum problem.
Could someone help me, please?
=========
UPDATE
Based on #luizpauloml comments, I am updating this post.
I need to convert the second-order ODE into a system of first-order ODEs and then I need to write a function to represent such system.
So, I know how to do this on pen and paper. Hence, using z as a variable:
OK, but how do I write a normal script?
The Xcos is quite disposable. I only kept it because I was trying to mimic the example on the official Scilab page.
To solve this, you need to use ode(), which can employ many methods, Runge-Kutta included. First, you need to define a function to represent the system of ODEs, and Step 1 in the link you provided shows you what to do:
function z = f(t,y)
//f(t,z) represents the sysmte of ODEs:
// -the first argument should always be the independe variable
// -the second argument should always be the dependent variables
// -it may have more than two arguments
// -y is a vector 2x1: y(1) = theta, y(2) = theta'
// -z is a vector 2x1: z(1) = z , z(2) = z'
z(1) = y(2) //first equation: z = theta'
z(2) = 10*sin(y(1)) //second equation: z' = 10*sin(theta)
endfunction
Notice that even if t (the independent variable) does not explicitly appear in your system of ODEs, it still needs to be an argument of f(). Now you just use ode(), setting the flag 'rk' or 'rkf' to use either one of the available Runge-Kutta methods:
ts = linspace(0,3,200);
theta0 = %pi/4;
dtheta0 = 0;
y0 = [theta0; dtheta0];
t0 = 0;
thetas = ode('rk',y0, t0, ts, f); //the output have the same order
//as the argument `y` of f()
scf(1); clf();
plot2d(thetas(2,:),thetas(1,:),-5);
xtitle('Phase portrait', 'theta''(t)','theta(t)');
xgrid();
The output:

Octave : fsolve function error

I have a problem when i use fsolve, the programm send me an error, but i don't know why. I should find for x(2) = 2.96e+13 and x(1)= 2.72e+22.
I tried this for solve my non linear equations : i have 2 equations and 2 unknows:
function y = f(x)
global x_rayon_droite
global z1
global r_droite
global x_rayon_gauche
global r_gauche
global y_rayon_droite
global y_rayon_gauche
x_rayon_droite=406
z1=2.72e+22
r_droite = 556.77
x_rayon_gauche=179
r_gauche = 241.42
y_rayon_droite=381
y_rayon_gauche=162
y(1)= x(1).*(x_rayon_droite./z1-x(2)./r_droite.*x_rayon_droite./r_droite)-x(1).*(x_rayon_gauche./z1-x(2)./r_gauche.*x_rayon_gauche./r_gauche)+x_rayon_droite-x_rayon_gauche;
y(2)= x(1).*(y_rayon_droite./z1-x(2)./r_droite.*y_rayon_droite./r_droite)-x(1).*(y_rayon_gauche./z1-x(2)./r_gauche.*y_rayon_gauche./r_gauche)+y_rayon_droite-y_rayon_gauche;
endfunction
[x,info]=fsolve("f",[1e+22;1e13])
Error after using fsolve:
Thx in advance
The first thing I'd do would be to change the call to
[x,val,info]=fsolve("f",[1e+22;1e13],options)
and experiment with options. In this example, it's easy to find the Jacobian of f analytically, so I'd provide that as the second output of f, and put options.Jacobian="on" Another potentially helpful option is AutoScaling = "on", to deal with variables of different scales.
But these don't help much in the specific example, because it's really badly scaled. Here's what the equations actually are:
0.0017615*x(1)*x(2) + (8.346e-21)*x(1) = -227
0.0015505*x(1)*x(2) + (8.051e-21)*x(1) = -219
This becomes a linear system if we introduce a new variable z = x(1)*x(2). But even as a linear system, it's so badly scaled than the solver is likely to give up. Instead, let's introduce z = (1e+15)*x(1)*x(2), which amounts to multiplying the first column by 1e-15. Then the linear solver returns
z = 96810.712
x(2) = -2.720e+22
and so x(1) = -3.559e-33
This is kind of insane scale disparity, which makes one wonder if the results are meaningful at all, given that you had 3-5 significant digits in your coefficients.

How to determine width of peaks and make FFT for every peak (and plot it in separate graph)

I have an acceleration data for X-axis and time vector for it. I determined the peaks more than threshold and now I should find the FFT for every peak.
As result I have this:
Peak Value 1 = 458, index 1988
Peak Value 2 = 456, index 1990
Peak Value 3 = 450, index 12081
....
Peak Value 9 = 432, index 12151
To find these peaks I used the peakfinder script.
The command [peakLoc, peakMag] = peakfinder(x0,...) gives me location and magnitude of peaks.
Also I have the Time (from time data vector) for each peak.
So what I suppose, that I should take every peak, find its width (or some data points around the peak) and make the FFT. Am I right? Could you help me in that?
I'm working in Octave and I'm new here :)
Code:
load ("C:\\..patch..\\peakfinder.m");
d =dlmread("C:\\..patch..\\acc2.csv", ";");
T=d(:,1);
Ax=d(:,2);
[peakInd peakVal]=peakfinder(Ax,10,430,1);
peakTime=T(peakInd);
[sortVal sortInd] = sort(peakVal, 'descend');
originInd = peakInd(sortInd);
for k = 1 : length(sortVal)
fprintf(1, 'Peak #%d = %d, index%d\n', k, sortVal(k), originInd (k));
end
plot(T,Ax,'b-',T(peakInd),Ax(peakInd),'rv');
and here you can download the data http://www.filedropper.com/acc2
FFT
d =dlmread("C:\\..path..\\acc2.csv", ";");
T=d(:,1);
Ax=d(:,2);
% sampling frequency
Fs_a=2000;
% length of FFT
Length_Ax=numel(Ax);
% number of lines of Fourier spectrum
fft_L= Fs_a*2;
% an array of time samples
T_Ax=0:1/Fs_a: Length_Ax;
fft_Ax=abs(fft(Ax,fft_L));
fft_Ax=2*fft_Ax./fft_L;
F=0:Fs_a/fft_L:Fs_a/2-1/fft_L;
subplot(3,1,1);
plot(T,Ax);
title('Ax axis');
xlabel('time (s)');
ylabel('amplitude)'); grid on;
subplot(3,1,2);
plot(F,fft_Ax(1:length(F)));
title('spectrum max Ax axis');
xlabel('frequency (Hz)');
ylabel('amplitude'); grid on;
It looks like you have two clusters of peaks, so I would plot the data over three plots: one of the whole timeseries, one zoomed in on the first cluster, and the last one zoomed in on the second cluster (note I have divided all your time values by 1e6 otherwise the tick labels get ugly):
figure
subplot(3,1,1)
plot(T/1e6,Ax,'b-',peakTime/1e6,peakVal,'rv');
subplot(3,1,2)
plot(T/1e6,Ax,'b-',peakTime(1:4)/1e6,peakVal(1:4),'rv');
axis([0.99*peakTime(1)/1e6 1.01*peakTime(4)/1e6 0.9*peakVal(1) 1.1*peakVal(4)])
subplot(3,1,3)
plot(T/1e6,Ax,'b-',peakTime(5:end)/1e6,peakVal(5:end),'rv');
axis([0.995*peakTime(5)/1e6 1.005*peakTime(end)/1e6 0.9*peakVal(5) 1.1*peakVal(end)])
I have set the axes around the extreme time and acceleration values, using some coefficients to have some "padding" around (the values of these coefficients were obtained through trial and error). This gives me the following plot, hopefully this is the sort of thing you are after. You can add x and y labels if you wish.
EDIT
Here's how I would do the FFT:
Fs = 2000;
L = length(Ax);
NFFT = 2^nextpow2(L); % Next power of 2 from length of Ax
Ax_FFT = fft(Ax,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
% Plot single-sided amplitude spectrum.
figure
semilogx(f,2*abs(Ax_FFT(1:NFFT/2+1))) % using semilogx as huge DC component
title('Single-Sided Amplitude Spectrum of Ax')
xlabel('Frequency (Hz)')
ylabel('|Ax(f)|')
ylim([0 300])
giving the following result:

How to compute Fourier coefficients with MATLAB

I'm trying to compute the Fourier coefficients for a waveform using MATLAB. The coefficients can be computed using the following formulas:
T is chosen to be 1 which gives omega = 2pi.
However I'm having issues performing the integrals. The functions are are triangle wave (Which can be generated using sawtooth(t,0.5) if I'm not mistaking) as well as a square wave.
I've tried with the following code (For the triangle wave):
function [ a0,am,bm ] = test( numTerms )
b_m = zeros(1,numTerms);
w=2*pi;
for i = 1:numTerms
f1 = #(t) sawtooth(t,0.5).*cos(i*w*t);
f2 = #(t) sawtooth(t,0.5).*sin(i*w*t);
am(i) = 2*quad(f1,0,1);
bm(i) = 2*quad(f2,0,1);
end
end
However it's not getting anywhere near the values I need. The b_m coefficients are given for a
triangle wave and are supposed to be 1/m^2 and -1/m^2 when m is odd alternating beginning with the positive term.
The major issue for me is that I don't quite understand how integrals work in MATLAB and I'm not sure whether or not the approach I've chosen works.
Edit:
To clairify, this is the form that I'm looking to write the function on when the coefficients have been determined:
Here's an attempt using fft:
function [ a0,am,bm ] = test( numTerms )
T=2*pi;
w=1;
t = [0:0.1:2];
f = fft(sawtooth(t,0.5));
am = real(f);
bm = imag(f);
func = num2str(f(1));
for i = 1:numTerms
func = strcat(func,'+',num2str(am(i)),'*cos(',num2str(i*w),'*t)','+',num2str(bm(i)),'*sin(',num2str(i*w),'*t)');
end
y = inline(func);
plot(t,y(t));
end
Looks to me that your problem is what sawtooth returns the mathworks documentation says that:
sawtooth(t,width) generates a modified triangle wave where width, a scalar parameter between 0 and 1, determines the point between 0 and 2π at which the maximum occurs. The function increases from -1 to 1 on the interval 0 to 2πwidth, then decreases linearly from 1 to -1 on the interval 2πwidth to 2π. Thus a parameter of 0.5 specifies a standard triangle wave, symmetric about time instant π with peak-to-peak amplitude of 1. sawtooth(t,1) is equivalent to sawtooth(t).
So I'm guessing that's part of your problem.
After you responded I looked into it some more. Looks to me like it's the quad function; not very accurate! I recast the problem like this:
function [ a0,am,bm ] = sotest( t, numTerms )
bm = zeros(1,numTerms);
am = zeros(1,numTerms);
% 2L = 1
L = 0.5;
for ii = 1:numTerms
am(ii) = (1/L)*quadl(#(x) aCos(x,ii,L),0,2*L);
bm(ii) = (1/L)*quadl(#(x) aSin(x,ii,L),0,2*L);
end
ii = 0;
a0 = (1/L)*trapz( t, t.*cos((ii*pi*t)/L) );
% now let's test it
y = ones(size(t))*(a0/2);
for ii=1:numTerms
y = y + am(ii)*cos(ii*2*pi*t);
y = y + bm(ii)*sin(ii*2*pi*t);
end
figure; plot( t, y);
end
function a = aCos(t,n,L)
a = t.*cos((n*pi*t)/L);
end
function b = aSin(t,n,L)
b = t.*sin((n*pi*t)/L);
end
And then I called it like:
[ a0,am,bm ] = sotest( t, 100 );
and I got:
Sweetness!!!
All I really changed was from quad to quadl. I figured that out by using trapz which worked great until the time vector I was using didn't have enough resolution, which led me to believe it was a numerical issue rather than something fundamental. Hope this helps!
To troubleshoot your code I would plot the functions you are using and investigate, how the quad function samples them. You might be undersampling them, so make sure your minimum step size is smaller than the period of the function by at least factor 10.
I would suggest using the FFTs that are built-in to Matlab. Not only is the FFT the most efficient method to compute a spectrum (it is n*log(n) dependent on the length n of the array, whereas the integral in n^2 dependent), it will also give you automatically the frequency points that are supported by your (equally spaced) time data. If you compute the integral yourself (might be needed if datapoints are not equally spaced), you might calculate frequency data that are not resolved (closer spacing than 1/over the spacing in time, i.e. beyond the 'Fourier limit').