I'm going to start off by stating that, yes, this is homework (my first homework question on stackoverflow!). But I don't want you to solve it for me, I just want some guidance!
The equation in question is this:
I'm told to take N = 50, phi1 = 300, phi2 = 400, 0<=x<=1, and 0<=y<=1, and to let x and y be vectors of 100 equally spaced points, including the end points.
So the first thing I did was set those variables, and used x = linspace(0,1) and y = linspace(0,1) to make the correct vectors.
The question is Write a MATLAB script file called potential.m which calculates phi(x,y) and makes a filled contour plot versus x and y using the built-in function contourf (see the help command in MATLAB for examples). Make sure the figure is labeled properly. (Hint: the top and bottom portions of your domain should be hotter at about 400 degrees versus the left and right sides which should be at 300 degrees).
However, previously, I've calculated phi using either x or y as a constant. How am I supposed to calculate it where both are variables? Do I hold x steady, while running through every number in the vector of y, assigning that to a matrix, incrementing x to the next number in its vector after running through every value of y again and again? And then doing the same process, but slowly incrementing y instead?
If so, I've been using a loop that increments to the next row every time it loops through all 100 values. If I did it that way, I would end up with a massive matrix that has 200 rows and 100 columns. How would I use that in the linspace function?
If that's correct, this is how I'm finding my matrix:
clear
clc
format compact
x = linspace(0,1);
y = linspace(0,1);
N = 50;
phi1 = 300;
phi2 = 400;
phi = 0;
sum = 0;
for j = 1:100
for i = 1:100
for n = 1:N
sum = sum + ((2/(n*pi))*(((phi2-phi1)*(cos(n*pi)-1))/((exp(n*pi))-(exp(-n*pi))))*((1-(exp(-n*pi)))*(exp(n*pi*y(i)))+((exp(n*pi))-1)*(exp(-n*pi*y(i))))*sin(n*pi*x(j)));
end
phi(j,i) = phi1 - sum;
end
end
for j = 1:100
for i = 1:100
for n = 1:N
sum = sum + ((2/(n*pi))*(((phi2-phi1)*(cos(n*pi)-1))/((exp(n*pi))-(exp(-n*pi))))*((1-(exp(-n*pi)))*(exp(n*pi*y(j)))+((exp(n*pi))-1)*(exp(-n*pi*y(j))))*sin(n*pi*x(i)));
end
phi(j+100,i) = phi1 - sum;
end
end
This is the definition of contourf. I think I have to use contourf(X,Y,Z):
contourf(X,Y,Z), contourf(X,Y,Z,n), and contourf(X,Y,Z,v) draw filled contour plots of Z using X and Y to determine the x- and y-axis limits. When X and Y are matrices, they must be the same size as Z and must be monotonically increasing.
Here is the new code:
N = 50;
phi1 = 300;
phi2 = 400;
[x, y, n] = meshgrid(linspace(0,1),linspace(0,1),1:N)
f = phi1-((2./(n.*pi)).*(((phi2-phi1).*(cos(n.*pi)-1))./((exp(n.*pi))-(exp(-n.*pi)))).*((1-(exp(-1.*n.*pi))).*(exp(n.*pi.*y))+((exp(n.*pi))-1).*(exp(-1.*n.*pi.*y))).*sin(n.*pi.*x));
g = sum(f,3);
[x1,y1] = meshgrid(linspace(0,1),linspace(0,1));
contourf(x1,y1,g)
Vectorize the code. For example you can write f(x,y,n) with:
[x y n] = meshgrid(-1:0.1:1,-1:0.1:1,1:10);
f=exp(x.^2-y.^2).*n ;
f is a 3D matrix now just sum over the right dimension...
g=sum(f,3);
in order to use contourf, we'll take only the 2D part of x,y:
[x1 y1] = meshgrid(-1:0.1:1,-1:0.1:1);
contourf(x1,y1,g)
The reason your code takes so long to calculate the phi matrix is that you didn't pre-allocate the array. The error about size happens because phi is not 100x100. But instead of fixing those things, there's an even better way...
MATLAB is a MATrix LABoratory so this type of equation is pretty easy to compute using matrix operations. Hints:
Instead of looping over the values, rows, or columns of x and y, construct matrices to represent all the possible input combinations. Check out meshgrid for this.
You're still going to need a loop to sum over n = 1:N. But for each value of n, you can evaluate your equation for all x's and y's at once (using the matrices from hint 1). The key to making this work is using element-by-element operators, such as .* and ./.
Using matrix operations like this is The Matlab Way. Learn it and love it. (And get frustrated when using most other languages that don't have them.)
Good luck with your homework!
Related
I was asked to do circular convolution between two functions by sampling them, using the functions cconv. A known result of this sort of convolution is: CCONV( sin(x), sin(x) ) == -pi*cos(x)
To test the above I did:
w = linspace(0,2*pi,1000);
l = linspace(0,2*pi,1999);
stem(l,cconv(sin(w),sin(w))
but the result I got was:
which is absolutely not -pi*cos(x).
Can anybody please explain what is wrong with my code and how to fix it?
In the documentation of cconv it says that:
c = cconv(a,b,n) circularly convolves vectors a and b. n is the length of the resulting vector. If you omit n, it defaults to length(a)+length(b)-1. When n = length(a)+length(b)-1, the circular convolution is equivalent to the linear convolution computed with conv.
I believe that the reason for your problem is that you do not specify the 3rd input to cconv, which then selects the default value, which is not the right one for you. I have made an animation showing what happens when different values of n are chosen.
If you compare my result for n=200 to your plot you will see that the amplitude of your data is 10 times larger whereas the length of your linspace is 10 times bigger. This means that some normalization is needed, likely a multiplication by the linspace step.
Indeed, after proper scaling and choice of n we get the right result:
res = 100; % resolution
w = linspace(0,2*pi,res);
dx = diff(w(1:2)); % grid step
stem( linspace(0,2*pi,res), dx * cconv(sin(w),sin(w),res) );
This is the code I used for the animation:
hF = figure();
subplot(1,2,1); hS(1) = stem(1,cconv(1,1,1)); title('Autoscaling');
subplot(1,2,2); hS(2) = stem(1,cconv(1,1,1)); xlim([0,7]); ylim(50*[-1,1]); title('Constant limits');
w = linspace(0,2*pi,100);
for ind1 = 1:200
set(hS,'XData',linspace(0,2*pi,ind1));
set(hS,'YData',cconv(sin(w),sin(w),ind1));
suptitle("n = " + ind1);
drawnow
% export_fig(char("D:\BLABLA\F" + ind1 + ".png"),'-nocrop');
end
I am trying to draw a curve where each points are specific distance away from each other.
Now, blow shows pretty much what I wanted to do but I want sin like curve and not constant radius.
R = 50; %radius
Gap = 0.1; % gap between points
Curve = 180;
rad = 0;
n= pi*2*R*Curve/360/Gap; % n is length of arc
th = linspace( pi, rad ,n);
x = R*cos(th)+R;
y = R*sin(th)+100;
PathDB.Route1.x(1:1001,1)=0;
PathDB.Route1.y = (0:Gap:100)';
LengthY = length(PathDB.Route1.y);
PathDB.Route1.x(1001:1001+length(x)-1,1)=x ;
PathDB.Route1.y(LengthY:LengthY+length(y)-1) = y;
LengthX = length(PathDB.Route1.x);
LengthY = length(PathDB.Route1.y);
PathDB.Route1.x(LengthX:LengthX+1000,1)=PathDB.Route1.x(LengthX,1);
PathDB.Route1.y(LengthY:LengthY+1000,1)= (PathDB.Route1.y(LengthY,1):-Gap:0);
plot(PathDB.Route1.x, PathDB.Route1.y);
grid ;
axis equal
All I want to do is instead of perfect curve, I want to add sin like curve which are plotted by 0.1.
I'm sorry about my poor coding skills I hope you can understand and help me.
Any advice is appreciated!
Rui
If you want this to be robust, you'll probably need to use the Matlab Symbolic Toolbox. If you take a function f, and want to plot equidistant points along it's curve between a and b, you could do something like this (using sin(x) as the example function):
sym x;
f = sin(x);
g = diff(f); %derivative of f
b=5;
a=0;
%check syntax on this part
arc_length = int(sqrt(1+g^2)); %create function for arc_length
Sc = arc_length(b)-arc_length(a); %calculate total arc length
n = 30 %number of points to plot (not including b)
Sdist = Sc/n; %distance between points
xvals = zeros(1,(n+1)); %initialize array of x values, n+1 to include point at b
Next, we will want to iterate through the intervals to find equidistant points. To do this we take each S_ij where i represents the x value of the low end of the interval and j represents the high end. We start with i=a and stop when i=b. Using the inverse of our arc length equation we can solve for j. Sdist == S_ij => Sdist = arc_length(j) - arc_length(i) => Sdist+arc_length(i)=arc_length(j). Using this, we can compute: j = arc_length_inverse(Sdist+arc_length(i)). One way of implementing this would be as follows:
arc_length_inv = finverse(arc_length);
i=a
xvals(1)=i;
for ii=1:n
j = arc_length_inv(Sdist+arc_length(i)); %calculate high end
i = j %make high end new low end
xvals(ii+1)=i; %put new low end value in x array
end
With this array of x values, you can compute yvals=sin(xvals) and plot(xvals,yvals)
Some of this code probably needs tweaking but the math should be sound (the formula for arc length came from http://en.wikipedia.org/wiki/Arc_length). Mathematically, this should work for any function integrable on [a,b] and whose arc_length function has an inverse; that said, I do not know the capabilities of the int() and finverse() functions.
If you do not need robustness you can follow these steps for your specific function, doing the math by hand and entering the functions manually;
I have a vector with different values.
Some of the values are zeros and sometimes they even come one after another.
I need to plot this vector against another vector with the same size but I can't have zeros in it.
What is the best way I can do some kind of interpolation to my vector and how do I do it?
I tried to read about interpolation in mat-lab but I didn't understand good enough to implement it.
If it's possible to explain it to me step by step I will be grateful since I'm new with this program.
Thanks
Starting from a dataset consisting of two equal length vectors x,y, where y values equal to zero are to be excluded, first pick the subset excluding zeros:
incld = y~=0;
Then you interpolate over that subset:
yn = interp1(x(incld),y(incld),x);
Example result, plotting x against y (green) and x against yn (red):
edit
Notice that, by the definition of interpolation, if terminal points are zero, you will have to take care of that separately, for instance by running the following before the lines above:
if y(1)==0, y(1) = y(find(y~=0,1,'first'))/2; end
if y(end)==0, y(end) = y(find(y~=0,1,'last'))/2; end
edit #2
And this is the 2D version of the above, where arrays X and Y are coordinates corresponding to the entries in 2D array Z:
[nr nc]=size(Z);
[X Y] = meshgrid([1:nc],[1:nr]);
X2 = X;
Y2 = Y;
Z2 = Z;
excld = Z==0;
X2(excld) = [];
Y2(excld) = [];
Z2(excld) = [];
ZN = griddata(X2,Y2,Z2,X,Y);
ZN contains the interpolated points.
In the figure below, zeros are shown by dark blue patches. Left is before interpolation, right is after:
i have created a function that represents a triangle sign.
this function does not work on vectors. i want to evaluate a vector x:
x=[-2:0.01:2]
and save the answer in vector y, for this purpose i came up with the following code:
for i=1:400, y(i) = triangle(x(i))
after i got the ans i plotted is using plot. in this case it worked ok but i am interested on observing the influence of time shifting and shrinking so when i try to use lets say:
for i=1:200, y(i) = triangle(x(2*i))
i get a vector y not the same length as vector x and i cant even plot them... is there any easy way to achieve it? and how should i plot the answer?
here is my function:
function [ out1 ] = triangle( input1 )
if abs(input1) < 1,
out1 = 1 - abs(input1);
else
out1 = 0;
end
end
y is a different length in each for loop because each loop iterated a different number of times. In the example below, I use the same for loops and plot y2 with the corresponding values of x. i is already defined in matlab so I've changed it to t in the example below.
clear all
x=[-2:0.01:2];
for t=1:400
y(t) = triangle(x(t));
end
for t=1:200
y2(t) = triangle(x(2*t));
end
Or, if you want to see y2 plotted over the same range you can increase the size of x:
clear all
x=[-2:0.01:8];
for t=1:400
y(t) = triangle(x(t));
end
for t=1:400
y2(t) = triangle(x(2*t));
end
plot(x(1:length(y)),y,'r')
hold on
plot(x(1:length(y2)),y2,'b')
my question is quite trivial, but I'm looking for the vectorized form of it.
My code is:
HubHt = 110; % Hub Height
GridWidth = 150; % Grid length along Y axis
GridHeight = 150; % Grid length along Z axis
RotorDiameter = min(GridWidth,GridHeight); % Turbine Diameter
Ny = 31;
Nz = 45;
%% GRID DEFINITION
dy = GridWidth/(Ny-1);
dz = GridHeight/(Nz-1);
if isequal(mod(Ny,2),0)
iky = [(-Ny/2:-1) (1:Ny/2)];
else
iky = -floor(Ny/2):ceil(Ny/2-1);
end
if isequal(mod(Nz,2),0)
ikz = [(-Nz/2:-1) (1:Nz/2)];
else
ikz = -floor(Nz/2):ceil(Nz/2-1);
end
[Y Z] = ndgrid(iky*dy,ikz*dz + HubHt);
EDIT
Currently I am using this solution, which has reasonable performances:
coord(:,1) = reshape(Y,[numel(Y),1]);
coord(:,2) = reshape(Z,[numel(Z),1]);
dist_y = bsxfun(#minus,coord(:,1),coord(:,1)');
dist_z = bsxfun(#minus,coord(:,2),coord(:,2)');
dist = sqrt(dist_y.^2 + dist_z.^2);
I disagree with Dan and Tal.
I believe you should use pdist rather than pdist2.
D = pdist( [Y(:) Z(:)] ); % a compact form
D = squareform( D ); % square m*n x m*n distances.
I agree with Tal Darom, pdist2 is exactly the function you need. It finds the distance for each pair of coordinates specified in two vectors and NOT the distance between two matrices.
So I'm pretty sure in your case you want this:
pdist2([Y(:), Z(:)], [Y(:), Z(:)])
The matrix [Y(:), Z(:)] is a list of every possible coordinate combination over the 2D space defined by Y-Z. If you want a matrix containing the distance from each point to each other point then you must call pdist2 on this matrix with itself. The result is a 2D matrix with dimensions numel(Y) x numel(Y) and although you haven't defined it I'm pretty sure that both Y and Z are n*m matrices meaning numel(Y) == n*m
EDIT:
A more correct solution suggested by #Shai is just to use pdist since we are comparing points within the same matrix:
pdist([Y(:), Z(:)])
You can use the matlab function pdist2 (I think it is in the statistics toolbox) or you can search online for open source good implementations of this function.
Also,
look at this unswer: pdist2 equivalent in MATLAB version 7