Numerically integrating in Matlab. Why does increasing divisions cause less accurate output? - matlab

I am currently working on a problem in Matlab where I am given a distributed loaded beam and then numerically integrate (using the composite trapezoidal rule) to find the shear force and bending moment. From there I find the maximum moment value and the position at which this occurs. Then I differentiate twice to again find the shear force and distributed load.
I am using the analytically integrated functions in order to check and make sure that each of my numerical integrations and differentiations are calculated correctly. My issue is that when I use only 13 points to perform the calculations (x = 0:12;) my values are as close as I have gotten them to the results from the analytical values. (This number comes from the beam being 12 ft and the divisions are 1 ft each). When I increase the number of divisions in order to increase the accuracy of my numerical integration the values get further away from the analytical values. I would like to figure out what could have caused this and thus far have been unsuccessful.
My code:
clear; clc;
% n=100;
% x = linspace(0,12,n+1); %dx (100 divisions)
x=0:12; %dx (12 divisions)
w = 12.5.*x; %distributed load
%//Integrated Shear
V(1)=300;
for i = 2: length(w)
weight = [.5 ones(1,i-2) .5];
V(i)=300-sum(weight.*w(1:i));
end
figure(1); clf;
plot(x,V,'--');
legend('Numerical Shear');
xlabel('Position (ft)'); ylabel('Shear Force (lb)');
%//Integrated Moment
Mactual = 300.*x - ((25/12).*(x.^3)); %analytically integrated M
M(1)=0;
for i = 2: length(V)
weight = [.5 ones(1,i-2) .5];
M(i)=sum(weight.*V(1:i));
end
figure(2); clf;
plot(x,M,'--'); hold on;
plot(x,Mactual); hold off;
legend('Numerical Moment' , 'Analytical Moment');
xlabel('Position (ft)'); ylabel('Bending Moment (lbf)');
%//Max Moment and Position at Max Moment
[maxMValue, indexAtMaxM] = max(M);
xValueAtMaxMValue = x(indexAtMaxM(1));
dispM = [ 'Maximum bending moment: ' , num2str(maxMValue) , ' lbf'];
disp(dispM);
dispX = [ 'Position at maximum bending moment: ' , num2str(xValueAtMaxMValue) , ' ft' ];
disp(dispX)
%//Derived Shear
dM(1)=300;
for i = 1:length(x)-1
dM(i+1) = (M(i+1)-M(i));
end
figure(3); clf;
plot(x,dM,'--'); hold on;
Vactual = 300 - 6.25.*(x.^2); %analytically integrated shear
plot(x,Vactual); hold off;
legend('Numerical Shear' , 'Analytical Shear');
xlabel('Position (ft)'); ylabel('Shear Force (lb)');
%//Derived Load
dV(1)=0;
for i = 1:length(x)-1
dV(i+1) = -(V(i+1)-V(i));
end
figure(4); clf;
plot(x,dV,'--'); hold on;
plot (x,w); hold off;
legend('Numerical Load' , 'Analytical Load');
xlabel('Position (ft)'); ylabel('Distributed Load (lb/ft)');
In the code above the 2nd and 3rd lines are commented out to show the output with only 12 divisions. If you uncomment those two lines and comment out the 4th line the output shows values with 100 divisions.
Quick Note: The analytical values are plotted against the integrated moment, derived shear, and derived distributed load as a comparison. Consider the analytical values (Mactual and Vactual) as the correct output values.
Any input which could help lead me to a way to solve this issue would be greatly appreciated.

To get the correct integral value, you will need to multiply the function value sums with dx=12/n or identically dx=x(2)-x(1) if n is different from 12.
(11/28) You can also simplify the trapezoidal integration loops as
V(1)=300
M(1)=0
for i=2:n
V(i) = V(i-1) - 0.5*dx*(w(i-1)+w(i))
M(i) = M(i-1) + 0.5*dx*(V(i-1)+V(i))
end
thus avoiding reconstruction of the weights and computation of the sums in each step. This reduces this part of the algorithm from O(n^2) to O(n).

Related

how to replace the ode45 method with the runge-kutta in this matlab?

I tried everything and looked everywhere but can't find any solution for my question.
clc
clear all
%% Solving the Ordinary Differential Equation
G = 6.67408e-11; %Gravitational constant
M = 10; %Mass of the fixed object
r = 1; %Distance between the objects
tspan = [0 100000]; %Time Progression from 0 to 100000s
conditions = [1;0]; %y0= 1m apart, v0=0 m/s
F=#(t,y)var_r(y,G,M,r);
[t,y]=ode45(F,tspan,conditions); %ODE solver algorithm
%%part1: Plotting the Graph
% plot(t,y(:,1)); %Plotting the Graph
% xlabel('time (s)')
% ylabel('distance (m)')
%% part2: Animation of Results
plot(0,0,'b.','MarkerSize', 40);
hold on %to keep the first graph
for i=1:length(t)
k = plot(y(i,1),0,'r.','MarkerSize', 12);
pause(0.05);
axis([-1 2 -2 2]) %Defining the Axis
xlabel('X-axis') %X-Axis Label
ylabel('Y-axis') %Y-Axis Label
delete(k)
end
function yd=var_r(y,G,M,r) %function of variable r
g = (G*M)/(r + y(1))^2;
yd = [y(2); -g];
end
this is the code where I'm trying to replace the ode45 with the runge kutta method but its giving me errors. my runge kutta function:
function y = Runge_Kutta(f,x0,xf,y0,h)
n= (xf-x0)/h;
y=zeros(n+1,1);
x=(x0:h:xf);
y(1) = y0;
for i=1:n
k1 = f(x(i),y(i));
k2= f(x(i)+ h/2 , y(i) +h*(k1)/2);
y(i+1) = y(i)+(h*k2);
end
plot(x,y,'-.M')
legend('RKM')
title ('solution of y(x)');
xlabel('x');
ylabel('y(x)')
hold on
end
Before converting your ode45( ) solution to manually written RK scheme, it doesn't even look like your ode45( ) solution is correct. It appears you have a gravitational problem set up where the initial velocity is 0 so a small object will simply fall into a large mass M on a line (rectilinear motion), and that is why you have scalar position and velocity.
Going with this assumption, r is something you should be calculating on the fly, not using as a fixed input to the derivative function. E.g., I would have expected something like this:
F=#(t,y)var_r(y,G,M); % get rid of r
:
function yd=var_r(y,G,M) % function of current position y(1) and velocity y(2)
g = (G*M)/y(1)^2; % gravity accel based on current position
yd = [y(2); -g]; % assumes y(1) is positive, so acceleration is negative
end
The small object must start with a positive initial position for the derivative code to be valid as you have it written. As the small object falls into the large mass M, the above will only hold until it hits the surface or atmosphere of M. Or if you model M as a point mass, then this scheme will become increasingly difficult to integrate correctly because the acceleration becomes large without bound as the small mass gets very close to the point mass M. You would definitely need a variable step size approach in this case. The solution becomes invalid if it goes "through" mass M. In fact, once the speed gets too large the whole setup becomes invalid because of relativistic effects.
Maybe you could explain in more detail if your system is supposed to be set up this way, and what the purpose of the integration is. If it is really supposed to be a 2D or 3D problem, then more states need to be added.
For your manual Runge-Kutta code, you completely forgot to integrate the velocity so this is going to fail miserably. You need to carry a 2-element state from step to step, not a scalar as you are currently doing. E.g., something like this:
y=zeros(2,n+1); % 2-element state as columns of the y variable
x=(x0:h:xf);
y(:,1) = y0; % initial state is the first 2-element column
% change all the scalar y(i) to column y(:,i)
for i=1:n
k1 = f(x(i),y(:,i));
k2= f(x(i)+ h/2 , y(:,i) +h*(k1)/2);
y(:,i+1) = y(:,i)+(h*k2);
end
plot(x,y(1,:),'-.M') % plot the position part of the solution
This is all assuming the f that gets passed in is the same F you have in your original code.
y(1) is the first scalar element in the data structure of y (this counts in column-first order). You want to generate in y a list of column vectors, as your ODE is a system with state dimension 2. Thus you need to generate y with that format, y=zeros(length(x0),n+1); and then address the list entries as matrix columns y(:,1)=x0 and the same modification in every place where you extract or assign a list entry.
Matlab introduce various short-cuts that, if used consequently, lead to contradictions (I think the script-hater rant (german) is still valid in large parts). Essentially, unlike in other systems, Matlab gives direct access to the underlying data structure of matrices. y(k) is the element of the underlying flat array (that is interpreted column-first in Matlab like in Fortran, unlike, e.g., Numpy where it is row-first).
Only the two-index access is to the matrix with its dimensions. So y(:,k) is the k-th matrix column and y(k,:) the k-th matrix row. The single-index access is nice for row or column vectors, but leads immediately to problems when collecting such vectors in lists, as these lists are automatically matrices.

How to generate numbers that follows normal distribution but w.r.t. time in Matlab

As time moves along x-axis, the output value (may be freq or prob) be shown accordingly i.e. as time moves the values should increase initially till mean and then decrease. I have prespecified mean mu=10 and standard deviation sigma=2.
Do you mean you want to plot a Gaussian function? I am not sure if you also wanted some sort of animation. If not, you can modify the code below to turn off modification.
clear; close all; clc;
% Set the mean and standard deviation
mu = 10;
sigma = 2;
% Time t
t = linspace(0,20,101);
% The equation for a normal distribution
f = 1/(sqrt(2*pi)*sigma)*exp( -(t-mu).^2/(2*sigma^2));
hold on;
xlim([0,20]);
ylim([0,0.25]);
axis manual;
for i=1:length(t)
plot( t(1:i), f(1:i), 'b-');
pause(0.1);
end
hold off;

plotting line between points in a loop goes wrong

I am currently trying to simulate a random walk. The idea is to choose a random number between 0 and 2*pi and let the random walker go in that direction. Here is what I tried to do to simulate such a random walk:
before=[0 0]; %start in (0,0)
while 1
x=rand;
x=x*2*pi; %// choose random angle
increment=[cos(x),sin(x)] %// increments using the sine and cosine function
now=before+increment;
plot(before, now)
hold on
before=now;
pause(1);
end
I expect this program to plot lines and each new line starts at the ending point of the previous line, but this does not happen. I have no clue why it is not working.
You got the syntax for plot wrong, which is plot(X,Y). Change the call to
plot([before(1), now(1)], [before(2), now(2)])
and your program should work as expected.
Here is an improved version that does all the calculation vectorized and gives you two choices of output. The first one displays all at once and is very fast. The second one takes a lot of time depending on the amount of samples.
pts = [0,0]; % starting point
N = 10000; % sample count
x = rand(N,1) * 2*pi; % random angle
% calculate increments and points
inc = [cos(x),sin(x)];
pts = [pts;cumsum(inc,1)];
% plot result at once
figure;
plot(pts(:,1),pts(:,2));
% plot results in time steps
figure; hold on;
for i = 1:size(pts,1)
plot(pts(i:i+1,1),pts(i:i+1,2))
pause(1)
end
Here is an example of the output:

How to plot phasors of signals?

I have 3 singals and I'm trying to plot their phasors and their sum. I need to plot them end to end to demonstrate phasor addition. That is, the first phasor must start from the origin. The second phasor must start from the end of the first phasor. The third phasor must start from the end of the second one. In this way, the end point of the third phasor is the resulting phasor (considering that it starts at the origin). Horizontal and vertical axes are the real and imaginary axes, respectively in range of [-30, 30].
I just started using matlab today and this is due the night. I tried using plot, plot2, plot3, compass, and several ways but with all of them i failed. Compass was the closest to success.
I have amplitude and phase values of each phasor.
So how can I accomplish this task? Can you help me to draw two of phasors?
Any help is appreciated.
Thank you!
Related Example: from http://fourier.eng.hmc.edu/e84/lectures/ch3/node2.html
[example by spektre]
The following example should get you started:
First, the three phasors are defined.
% Define three complex numbers by magnitude and phase
ph1 = 20*exp(1i*0.25*pi);
ph2 = 10*exp(1i*0.7*pi);
ph3 = 5*exp(1i*1.2*pi);
Then, using cumsum, a vector containing ph1, ph1+ph2, ph1+ph2+ph3 is calculated.
% Step-wise vector sum
vecs = cumsum([ph1; ph2; ph3]);
vecs = [0; vecs]; % add origin as starting point
The complex numbers are plotted by real and imaginary part.
% Plot
figure;
plot(real(vecs), imag(vecs), '-+');
xlim([-30 30]);
ylim([-30 30]);
xlabel('real part');
ylabel('imaginary part');
grid on;
This produces the following figure:
figure(1); hold on;
ang = [0.1 0.2 0.7] ; % Angles in rad
r = [1 2 4] ; % Vector of radius
start = [0 0]
for i=1:numel(r)
plot([start(1) start(1)+r(i)*cos(ang(i))],[start(2) start(2)+r(i)*sin(ang(i))],'b-+')
start=start+[r(i)*cos(ang(i)) r(i)*sin(ang(i))]
end
plot([0 start(1)],[0 start(2)],'r-')

How to fit a curve by a series of segmented lines in Matlab?

I have a simple loglog curve as above. Is there some function in Matlab which can fit this curve by segmented lines and show the starting and end points of these line segments ? I have checked the curve fitting toolbox in matlab. They seems to do curve fitting by either one line or some functions. I do not want to curve fitting by one line only.
If there is no direct function, any alternative to achieve the same goal is fine with me. My goal is to fit the curve by segmented lines and get locations of the end points of these segments .
First of all, your problem is not called curve fitting. Curve fitting is when you have data, and you find the best function that describes it, in some sense. You, on the other hand, want to create a piecewise linear approximation of your function.
I suggest the following strategy:
Split manually into sections. The section size should depend on the derivative, large derivative -> small section
Sample the function at the nodes between the sections
Find a linear interpolation that passes through the points mentioned above.
Here is an example of a code that does that. You can see that the red line (interpolation) is very close to the original function, despite the small amount of sections. This happens due to the adaptive section size.
function fitLogLog()
x = 2:1000;
y = log(log(x));
%# Find section sizes, by using an inverse of the approximation of the derivative
numOfSections = 20;
indexes = round(linspace(1,numel(y),numOfSections));
derivativeApprox = diff(y(indexes));
inverseDerivative = 1./derivativeApprox;
weightOfSection = inverseDerivative/sum(inverseDerivative);
totalRange = max(x(:))-min(x(:));
sectionSize = weightOfSection.* totalRange;
%# The relevant nodes
xNodes = x(1) + [ 0 cumsum(sectionSize)];
yNodes = log(log(xNodes));
figure;plot(x,y);
hold on;
plot (xNodes,yNodes,'r');
scatter (xNodes,yNodes,'r');
legend('log(log(x))','adaptive linear interpolation');
end
Andrey's adaptive solution provides a more accurate overall fit. If what you want is segments of a fixed length, however, then here is something that should work, using a method that also returns a complete set of all the fitted values. Could be vectorized if speed is needed.
Nsamp = 1000; %number of data samples on x-axis
x = [1:Nsamp]; %this is your x-axis
Nlines = 5; %number of lines to fit
fx = exp(-10*x/Nsamp); %generate something like your current data, f(x)
gx = NaN(size(fx)); %this will hold your fitted lines, g(x)
joins = round(linspace(1, Nsamp, Nlines+1)); %define equally spaced breaks along the x-axis
dx = diff(x(joins)); %x-change
df = diff(fx(joins)); %f(x)-change
m = df./dx; %gradient for each section
for i = 1:Nlines
x1 = joins(i); %start point
x2 = joins(i+1); %end point
gx(x1:x2) = fx(x1) + m(i)*(0:dx(i)); %compute line segment
end
subplot(2,1,1)
h(1,:) = plot(x, fx, 'b', x, gx, 'k', joins, gx(joins), 'ro');
title('Normal Plot')
subplot(2,1,2)
h(2,:) = loglog(x, fx, 'b', x, gx, 'k', joins, gx(joins), 'ro');
title('Log Log Plot')
for ip = 1:2
subplot(2,1,ip)
set(h(ip,:), 'LineWidth', 2)
legend('Data', 'Piecewise Linear', 'Location', 'NorthEastOutside')
legend boxoff
end
This is not an exact answer to this question, but since I arrived here based on a search, I'd like to answer the related question of how to create (not fit) a piecewise linear function that is intended to represent the mean (or median, or some other other function) of interval data in a scatter plot.
First, a related but more sophisticated alternative using regression, which apparently has some MATLAB code listed on the wikipedia page, is Multivariate adaptive regression splines.
The solution here is to just calculate the mean on overlapping intervals to get points
function [x, y] = intervalAggregate(Xdata, Ydata, aggFun, intStep, intOverlap)
% intOverlap in [0, 1); 0 for no overlap of intervals, etc.
% intStep this is the size of the interval being aggregated.
minX = min(Xdata);
maxX = max(Xdata);
minY = min(Ydata);
maxY = max(Ydata);
intInc = intOverlap*intStep; %How far we advance each iteraction.
if intOverlap <= 0
intInc = intStep;
end
nInt = ceil((maxX-minX)/intInc); %Number of aggregations
parfor i = 1:nInt
xStart = minX + (i-1)*intInc;
xEnd = xStart + intStep;
intervalIndices = find((Xdata >= xStart) & (Xdata <= xEnd));
x(i) = aggFun(Xdata(intervalIndices));
y(i) = aggFun(Ydata(intervalIndices));
end
For instance, to calculate the mean over some paired X and Y data I had handy with intervals of length 0.1 having roughly 1/3 overlap with each other (see scatter image):
[x,y] = intervalAggregate(Xdat, Ydat, #mean, 0.1, 0.333)
x =
Columns 1 through 8
0.0552 0.0868 0.1170 0.1475 0.1844 0.2173 0.2498 0.2834
Columns 9 through 15
0.3182 0.3561 0.3875 0.4178 0.4494 0.4671 0.4822
y =
Columns 1 through 8
0.9992 0.9983 0.9971 0.9955 0.9927 0.9905 0.9876 0.9846
Columns 9 through 15
0.9803 0.9750 0.9707 0.9653 0.9598 0.9560 0.9537
We see that as x increases, y tends to decrease slightly. From there, it is easy enough to draw line segments and/or perform some other kind of smoothing.
(Note that I did not attempt to vectorize this solution; a much faster version could be assumed if Xdata is sorted.)