Ideas for reducing the complexity of a 3D density function for generating a ternary surface plot in Matlab - matlab

I have a 3D density function q(x,y,z) that I am trying to plot in Matlab 8.3.0.532 (R2014a).
The domain of my function starts at a and ends at b, with uniform spacing ds. I want to plot the density on a ternary surface plot, where each dimension in the plot represents the proportion of x,y,z at a given point. For example, if I have a unit of density on the domain at q(1,1,1) and another unit of density on the domain at q(17,17,17), in both cases there is equal proportions of x,y,z and I will therefore have two units of density on my ternary surface plot at coordinates (1/3,1/3,1/3). I have code that works using ternsurf. The problem is that the number of proportion points grows exponentially fast with the size of the domain. At the moment I can only plot a domain of size 10 (in each dimension) with unit spacing (ds = 1). However, I need a much larger domain than this (size 100 in each dimension) and much smaller than unit spacing (ideally as small as 0.1) - this would lead to 100^3 * (1/0.1)^3 points on the grid, which Matlab just cannot handle. Does anyone have any ideas about how to somehow bin the density function by the 3D proportions to reduce the number of points?
My working code with example:
a = 0; % start of domain
b = 10; % end of domain
ds = 1; % spacing
[x, y, z] = ndgrid((a:ds:b)); % generate 3D independent variables
n = size(x);
q = zeros(n); % generate 3D dependent variable with some norm distributed density
for i = 1:n(1)
for j = 1:n(2)
for k = 1:n(2)
q(i,j,k) = exp(-(((x(i,j,k) - 10)^2 + (y(i,j,k) - 10)^2 + (z(i,j,k) - 10)^2) / 20));
end
end
end
Total = x + y + z; % calculate the total of x,y,z at every point in the domain
x = x ./ Total; % find the proportion of x at every point in the domain
y = y ./ Total; % find the proportion of y at every point in the domain
z = z ./ Total; % find the proportion of z at every point in the domain
x(isnan(x)) = 0; % set coordinate (0,0,0) to 0
y(isnan(y)) = 0; % set coordinate (0,0,0) to 0
z(isnan(z)) = 0; % set coordinate (0,0,0) to 0
xP = reshape(x,[1, numel(x)]); % create a vector of the proportions of x
yP = reshape(y,[1, numel(y)]); % create a vector of the proportions of y
zP = reshape(z,[1, numel(z)]); % create a vector of the proportions of z
q = reshape(q,[1, numel(q)]); % create a vector of the dependent variable q
ternsurf(xP, yP, q); % plot the ternary surface of q against proportions
shading(gca, 'interp');
colorbar
view(2)

I believe you meant n(3) in your innermost loop. Here are a few tips:
1) Loose the loops:
q = exp(- ((x - 10).^2 + (y - 10).^2 + (z - 10).^2) / 20);
2) Loose the reshapes:
xP = x(:); yP = y(:); zP = z(:);
3) Check Total once, instead of doing three checks on x,y,z:
Total = x + y + z; % calculate the total of x,y,z at every point in the domain
Total( abs(Total) < eps ) = 1;
x = x ./ Total; % find the proportion of x at every point in the domain
y = y ./ Total; % find the proportion of y at every point in the domain
z = z ./ Total; % find the proportion of z at every point in the domain
PS: I just recognized your name.. it's Jonathan ;)

Discretization method probably depends on use of your plot, maybe it make sense to clarify your question from this point of view.
Overall, you probably struggling with an "Out of memory" error, a couple of relevant tricks are described here http://www.mathworks.nl/help/matlab/matlab_prog/resolving-out-of-memory-errors.html?s_tid=doc_12b?refresh=true#brh72ex-52 . Of course, they work only up to certain size of arrays.
A more generic solution is too save parts of arrays on hard drive, it makes processing slower but it'll work. E.g., you can define several q functions with the scale-specific ngrids (e.g. ngridOrder0=[0:10:100], ngridOrder10=[1:1:9], ngridOrder11=[11:1:19], etc... ), and write an accessor function which will load/save the relevant grid and q function depending on the part of the plot you're looking.

Related

Graphing electric potential of a ring of charge using MATLAB

My code is not plotting the correct contour plot for a plane perpendicular to a ring of charge running through its center properly. My problem is that the contour plot is not filling 2D space.
I've made two versions of code, one uses a for loop to calculate a
Riemann sum and the other simply uses the sum command. Both rely on the
'subs' command for substituting values from a meshgrid into my expression for V (electric potential).
Version 1 (using for loop):
%% Computing a symbolic expression for V for anywhere in space
syms x y z % phiprime is angle that an elemental dq of the circular
charge is located at, x,y and z are arbitrary points in space outside the
charge distribution
N = 200; % number of increments to sum
R = 2; % radius of circle is 2 meters
dphi = 2*pi/N; % discretizing the circular line of charge which spans 2pi
integrand = 0;
for phiprime = 0:dphi:2*pi
% phiprime ranges from 0 to 2pi in increments of dphi
integrand = integrand + dphi./(sqrt(((x - R.*cos(phiprime) )).^2 + ((y -
R.*sin(phiprime) ).^2) + z.^2));
end
intgrl = sum(integrand);
% uncessary but harmless step that I leave to show that I am using the
sum of the above expression for each dphi
eps0 = 8.854e-12;
kC = 1/(4*pi*eps0);
rhol = 1*10^-9; % linear charge density
Vtot = kC*rhol*R.*intgrl; % symbolic expression for Vtot
%% Graphing V & E in plane perpedicular to ring & passing through center
[Y1, Z1] = meshgrid(-4:.5:4, -4:.5:4);
Vcont1 = subs(Vtot, [x,y,z], {0,Y1,Z1}); % Vcont1 stands for V contour; 1
is because I do the plane of the ring next
contour(Y1,Z1,Vcont1)
xlabel('y - axis [m]')
ylabel('z - axis [m]')
title('V in a plane perpendicular to a ring of charge (N = 200)')
str = {'Red line is side view', 'of ring of charge'};
text(-1,2,str)
hold on
% visually displaying line of charge on plot
circle = rectangle('Position',[-2 0 4 .1],'Curvature',[1,1]);
set(circle,'FaceColor',[1, 0, 0],'EdgeColor',[1, 0, 0]);
% taking negative gradient of V and finding symbolic equations for Ex, Ey
and Ez
g = gradient(-1.*(kC*rhol*R.*intgrl),[x,y,z]);
%% now substituting all the values of the 2D coordinate system for the
symbolic x and y variables to get numeric values for Ex and Ey
Ey1 = subs(g(2), [x y z], {0,Y1,Z1});
Ez1 = subs(g(3), [x y z], {0,Y1,Z1});
E1 = sqrt(Ey1.^2 + Ez1.^2); % full numeric magnitude of E in y-z plane
Eynorm1 = Ey1./E1; % This normalizes the electric field lines
Eznorm1 = Ez1./E1;
quiver(Y1,Z1,Eynorm1,Eznorm1);
hold off
Version 2 (using sum command):
syms x y z
R = 2; % radius of circle is 2 meters
N=100;
dphi = 2*pi/N; % discretizing the circular line of charge which spans 2pi
phiprime = 0:dphi:2*pi; %phiprime ranges from 0 to 2pi in increments of
dphi
integrand = dphi./(sqrt(((x - R.*cos(phiprime) )).^2 + ((y -
R.*sin(phiprime) ).^2) + z.^2));
phiprime = 0:dphi:2*pi;
intgrl = sum(integrand); % Reimann sum performed here
eps0 = 8.854e-12;
kC = 1/(4*pi*eps0);
rhol = 1*10^-9; % linear charge density
Vtot = kC*rhol*R.*intgrl; % symbolic expression for Vtot
Everything else after that point for version 2 is the same as version 1 (substituting for the symbols x,y,z etc)
I would post images of what the code produces but apparently you need 10 reputation for that. Thanks stackoverflow. This will be much more confusing to understand without the images.
The vector field produced by my code is correct while the contour plot seems to use only a few points around the ends of the ring and connect them with straight lines in a strange diamond shape. I can't get it to fill space.
I receive no error messages. The contour lines accumulate around the ends of the ring (where the potential would approach infinity) in a strange diamond shape but aren't graphed anywhere else. I need the contour plot to fill the 2D grid
I received a solution to this question from MATLAB's community and posted about it here:
https://scicomp.stackexchange.com/questions/32834/graphing-electric-potential-of-a-ring-of-charge-using-matlab-help/32842#32842
I would post here but this "you can't post images because you don't have enough reputation" thing would make my explanation too abstract and difficult to understand so go take a look if you are having MATLAB contour plot issues and want to see my problem and solution

Matlab - plot speed vector of a satellite in Keplerian orbit

I have to plot the speed vector of an object orbiting around a central body. This is a Keplerian context. The trajectory of object is deduced from the classical formula ( r = p/(1+e*cos(theta)) with e=eccentricity.
I manage into plotting the elliptical orbit but now, I would like to plot for each point of this orbit the velocity speed of object.
To compute the velocity vector, I start from classical formulas (into polar coordinates), below the 2 components :
v_r = dr/dt and v_theta = r d(theta)/dt
To take a time step dt, I extract the mean anomaly which is proportional to time.
And Finally, I compute the normalization of this speed vector.
clear % clear variables
e = 0.8; % eccentricity
a = 5; % semi-major axis
b = a*sqrt(1-e^2); % semi-minor axis
P = 10 % Orbital period
N = 200; % number of points defining orbit
nTerms = 10; % number of terms to keep in infinite series defining
% eccentric anomaly
M = linspace(0,2*pi,N); % mean anomaly parameterizes time
% M varies from 0 to 2*pi over one orbit
alpha = zeros(1,N); % preallocate space for eccentric anomaly array
%%%%%%%%%%
%%%%%%%%%% Calculations & Plotting
%%%%%%%%%%
% Calculate eccentric anomaly at each point in orbit
for j = 1:N
% initialize eccentric anomaly to mean anomaly
alpha(j) = M(j);
% include first nTerms in infinite series
for n = 1:nTerms
alpha(j) = alpha(j) + 2 / n * besselj(n,n*e) .* sin(n*M(j));
end
end
% calcualte polar coordiantes (theta, r) from eccentric anomaly
theta = 2 * atan(sqrt((1+e)/(1-e)) * tan(alpha/2));
r = a * (1-e^2) ./ (1 + e*cos(theta));
% Compute cartesian coordinates with x shifted since focus
x = a*e + r.*cos(theta);
y = r.*sin(theta);
figure(1);
plot(x,y,'b-','LineWidth',2)
xlim([-1.2*a,1.2*a]);
ylim([-1.2*a,1.2*a]);
hold on;
% Plot 2 focus = foci
plot(a*e,0,'ro','MarkerSize',10,'MarkerFaceColor','r');
hold on;
plot(-a*e,0,'ro','MarkerSize',10,'MarkerFaceColor','r');
% compute velocity vectors
for i = 1:N-1
vr(i) = (r(i+1)-r(i))/(P*(M(i+1)-M(i))/(2*pi));
vtheta(i) = r(i)*(theta(i+1)-theta(i))/(P*(M(i+1)-M(i))/(2*pi));
vrNorm(i) = vr(i)/norm([vr(i),vtheta(i)],1);
vthetaNorm(i) = vtheta(i)/norm([vr(i),vtheta(i)],1);
end
% Plot velocity vector
quiver(x(30),y(30),vrNorm(30),vthetaNorm(30),'LineWidth',2,'MaxHeadSize',1);
% Label plot with eccentricity
title(['Elliptical Orbit with e = ' sprintf('%.2f',e)]);
Unfortunately, once plot performed, it seems that I get a bad vector for speed. Here for example the 30th element of vrNorm and vthetaNorm arrays :
As you can see, the vector has the wrong direction (If I assume to take 0 for theta from the right axis and positive variation like into trigonometrics).
If someone could see where is my error, this would nice.
UPDATE 1: Has this vector representing the speed on elliptical orbit to be tangent permanently to the elliptical curve ?
I would like to represent it by taking the right focus as origin.
UPDATE 2:
With the solution of #MadPhysicist, I have modified :
% compute velocity vectors
vr(1:N-1) = (2*pi).*diff(r)./(P.*diff(M));
vtheta(1:N-1) = (2*pi).*r(1:N-1).*diff(theta)./(P.*diff(M));
% Plot velocity vector
for l = 1:9 quiver(x(20*l),y(20*l),vr(20*l)*cos(vtheta(20*l)),vr(20*l)*sin(vtheta(20*l)),'LineWidth',2,'MaxHeadSize',1);
end
% Label plot with eccentricity
title(['Elliptical Orbit with e = ' sprintf('%.2f',e)]);
I get the following result :
On some parts of the orbit, I get wrong directions and I don't understand why ...
There are two issues with your code:
The normalization is done incorrectly. norm computes the generalized p-norm for a vector, which defaults to the Euclidean norm. It expects Cartesian inputs. Setting p to 1 means that it will just return the largest element of your vector. In your case, the normalization is meaningless. Just set vrNorm as
vrNorm = vr ./ max(vr)
It appears that you are passing in the polar coordinates vrNorm and vthetaNorm to quiver, which expects Cartesian coordinates. It's easy to make the conversion in a vectorized manner:
vxNorm = vrNorm * cos(vtheta);
vyNorm = vrNorm * sin(vtheta);
This assumes that I understand where your angle is coming from correctly and that vtheta is in radians.
Note
The entire loop
for i = 1:N-1
vr(i) = (r(i+1)-r(i))/(P*(M(i+1)-M(i))/(2*pi));
vtheta(i) = r(i)*(theta(i+1)-theta(i))/(P*(M(i+1)-M(i))/(2*pi));
vrNorm(i) = vr(i)/norm([vr(i),vtheta(i)],1);
vthetaNorm(i) = vtheta(i)/norm([vr(i),vtheta(i)],1);
end
can be rewritten in a fully vectorized manner:
vr = (2 * pi) .* diff(r) ./ (P .* diff(M))
vtheta = (2 * pi) .* r .* diff(theta) ./ (P .* diff(M))
vrNorm = vr ./ max(vr)
vxNorm = vrNorm * cos(vtheta);
vyNorm = vrNorm * sin(vtheta);
Note 2
You can call quiver in a vectorized manner, on the entire dataset, or on a subset:
quiver(x(20:199:20), y(20:199:20), vxNorm(20:199:20), vyNorm(20:199:20), ...)

Points distribution in n-dimension

How to distribute the points to be like Fig.A
This matlab code for Fig. B :
N = 30; % number of points
r = 0.5; % r = radius
d = 50; % dimension
C_point = 0; % center point
figure, clf
C = ones(1, d) * C_point;
C_rep = repmat( C,N,1);
X = randn(N,d);
s2 = sum(X.^2,2) ;
radius = r * (rand(N,1).^(1/d));
X = X.*repmat(radius./sqrt(s2),1,d) + C_rep;
%% Plot 2D
t = linspace(0, 2*pi, 100);
x = r*cos(t) + C(1);
y = r*sin(t) + C(2);
plot(x,y,'b')
hold on
plot(C(1),C(2),'b.', 'MarkerSize', 10) % center point
hold on
plot(X(:,1), X(:,2),'r.','markersize',10);
axis equal;rotate3d off; rotate3d on;drawnow;shg;
hold on
ax = axis;
Source of the code
What I should change to be like fig. A
The OP's code computes points uniformly distributed within a d-dimensional box, projects those onto a d-dimensional sphere, then samples the radius to move them inside the d-dimensional ball. This is perfect except that the points inside the box, when projected onto the sphere, do not form a uniform distribution on that sphere. If instead you find random points distributed in a Gaussian distribution, you are guaranteed uniform angle distribution.
First compute points with a Gaussian distribution in d dimensions (I do all here with minimal changes to the OP's code):
N = 1000; % number of points
r = 0.5; % r = radius
d = 3; % dimension
C_point = 0; % center point
C = ones(1,d) * C_point;
C_rep = repmat(C,N,1);
X = randn(N,d);
Note that I use randn, not rand. randn creates a Gaussian distribution.
Next we normalize the vectors so the points move to the sphere:
nX = sqrt(sum(X.^2,2));
X = X./repmat(nX,1,d);
These points are uniformly distributed, which you can verify by scatter3(X(:,1),X(:,2),X(:,3)); axis equal and turning the display around (a 2D rendering doesn't do it justice). This is the reason I set d=3 above, and N=1000. I wanted to be able to plot the points and see lots of them.
Next we compute, as you already did, a random distance to the origin, and correct it for the dimensionality:
radius = r * (rand(N,1).^(1/d));
X = X.*repmat(radius,1,d) + C_rep;
X now is distributed uniformly in the ball. Again, scatter3(X(:,1),X(:,2),X(:,3)); axis equal shows this.
However, if you set d=50 and then plot only two dimensions of your data, you will not see the data filling the circle. And you will not see a uniform distribution either. This is because you are projecting a 50-D ball onto 2 dimensions, this simply does not work. You either have to trust the math, or you have to slice the data:
figure, hold on
t = linspace(0, 2*pi, 100);
x = r*cos(t) + C(1);
y = r*sin(t) + C(2);
plot(x,y,'b')
plot(C(1),C(2),'b.', 'MarkerSize', 10) % center point
axis equal
I = all(abs(X(:,3:d))<0.1,2);
plot(X(I,1), X(I,2),'r.','markersize',10);
The I there indexes points that are close to the origin in dimensions perpendicular to the first two shown. Again, with d=50 you will have very few points there, so you will need to set N very large! To see the same density of points as in the case above, for every dimension you add, you need to multiply N by 10. So for d=5 you'd have N=1000*10*10=1e5, and for d=50 you'd need N=1e50. That is totally impossible to compute, of course.

Plotting cantilever and beam plots using Matlab

Problem
I have to plot a beam/cantilever using Matlab. Where my inputs are:
Length of the beam
Position of the loads (input is a vector)
Forces of the load (input is a vector)
Whether is it a cantilever or not. Because I have different equations for calculating the displacement.
My Solution
I have come to an idea on how I can actually plot the cantilever, but I can not formulate it into a code in MATLAB. I have spent hours trying to write something on Matlab but I have gotten nowhere. (I am a novice to Matlab)
My solution is as follow: I have the formula for the displacement from starting position.
I can define a vector using loop for x coordinates until the given beam length. Hence,
x=[0 ... L]
Then I want to define another vector where the difference is calculated (this is where I can't figure out Matlab)
y = [h, h - y(x1), h - y(x2), .... h - y(L)]
where h is the starting height, which I have thought to be defined as (y(x1) - y(L)) + 1, so that the graph then doesn't go into the negative axes. y(x) is the function which will calculate the displacement or fall of the beam.
Once that is done, then I can simply plot(x,y) and that would give me a graph of a shape of deflected beam for the given range from 0 to beam length. I have tested my theory on excel and it works as per the graph is concerned but I can not figure out implementation on Matlab.
My incomplete code
%Firstly we need the inputs
%Length of the beam
l = str2double(input('Insert the length of your beam: ', 's'));
%Now we need a vector for the positions of the load
a = [];
while 1
a(end+1) = input('Input the coordinate for the position of your load: ');
if length(a)>1; break; end
end
%Now we need a vector for the forces of the load
W = [];
while 1
W(end+1) = input('Input the forces of your load: ');
if length(W)>1; break; end
end
%
%
%
%Define the formula
y = ((W * (l - a) * x)/(6*E*I*l)) * (l^2 - x^2 - (l - a)^2);
%Where
E = 200*10^9;
I = 0.001;
%
%
%
%Now we try to plot
%Define a vector with the x values
vectx = [];
for i = 1:l
vectx = [vectx i];
end
%Now I want to calculate displacement for each x value from vectx
vecty = [];
for i=1:l
vecty=[10 - y(x(i)) i];
end
%Now I can plot all the information
plot(vectx, vecty)
hold on
%Now I plot the coordinate of the positions of the load
plot(load)
end
Really need some help/guidance. Would be truly grateful if someone can help me out or give me a hint :)
I have edited the question with further details
There are several things that do not work in your example.
For instance, parameters should be defined BEFORE you use them, so E and I should be defined before the deflection equation. And you should define x.
I do not understand why you put your inputs whithin a while loop, if you stop it at length(a)>1;. You can remove the loop.
You do not need a loop to calculate displacements, you can just use a substraction between vectors, like displacement = 10 - y. However, I do not understand what is H in your example; since your beam is initially at position 0, your displacement is just -y.
Finally, your equation to calculate the deformed shape is wrong; it only accounts for the first part of the beam.
Here, try if this code works:
%Length of the beam
l = input('Insert the length of your beam: ');
%Now we need a vector for the positions of the load
a = input('Input the coordinate for the position of your load: ');
%Now we need a vector for the forces of the load
W = input('Input the forces of your load: ');
%Define the formula
E = 200*10^9;
I = 0.001;
% x Position along the beam
x = linspace(0,l,100);
b = l - a;
% Deflection before the load position
pos = x <= a;
y(pos) = ((W * b .* x(pos))/(6*E*I*l)) .* (l^2 - x(pos).^2 - b^2);
% Cantilever option
% y(pos) = W*x(pos).^2/(6*E*I).*(3*a-x(pos));
% Deflection after the load position
pos = x > a;
y(pos) = ((W * b )/(6*E*I*l)) .* (l/b*(x(pos)-a).^3 + (l^2 - b^2)*x(pos) - x(pos).^3);
% Cantilever option
% y(pos) = W*a^2/(6*E*I).*(3*x(pos)-a);
displacement = 10 - y; % ???
% Plot beam
figure
plot(x , x .* 0 , 'k-')
hold on;
% Plot deflection
plot(x , y , '--')
% Plot load position
% Normalize arrow size as 1/10 of the beam length
quiver(a , 0 , 0 , sign(W) .* max(abs(y))/2)

Equally spaced points in a contour

I have a set of 2D points (not ordered) forming a closed contour, and I would like to resample them to 14 equally spaced points. It is a contour of a kidney on an image. Any ideas?
One intuitive approach (IMO) is to create an independent variable for both x and y. Base it on arc length, and interpolate on it.
% close the contour, temporarily
xc = [x(:); x(1)];
yc = [y(:); y(1)];
% current spacing may not be equally spaced
dx = diff(xc);
dy = diff(yc);
% distances between consecutive coordiates
dS = sqrt(dx.^2+dy.^2);
dS = [0; dS]; % including start point
% arc length, going along (around) snake
d = cumsum(dS); % here is your independent variable
perim = d(end);
Now you have an independent variable and you can interpolate to create N segments:
N = 14;
ds = perim / N;
dSi = ds*(0:N).'; %' your NEW independent variable, equally spaced
dSi(end) = dSi(end)-.005; % appease interp1
xi = interp1(d,xc,dSi);
yi = interp1(d,yc,dSi);
xi(end)=[]; yi(end)=[];
Try it using imfreehand:
figure, imshow('cameraman.tif');
h = imfreehand(gca);
xy = h.getPosition; x = xy(:,1); y = xy(:,2);
% run the above solution ...
Say your contour is defined by independent vector x and dependent vector y.
You can get your resampled x vector using linspace:
new_x = linspace(min(x),max(x),14); %14 to get 14 equally spaced points
Then use interp1 to get new_y values at each new_x point:
new_y = interp1(x,y,new_x);
There are a few interpolation methods to choose from - default is linear. See interp1 help for more info.