I know values of z on a (x,y) meshgrid, defined by xgrid, ygrid.
xgrid, and ygrid are not uniform grids.
I need to interpolate value of z off the grid points.
I need interpolants similar to 'pchip', because the problem at hand requires shape-preseving, and 'pchip' has better shape-preserving properties than e.g. 'spline'.
My current code:
xgrid = [0.01 0.1 0.5 1]';
ygrid = [0.01 0.1 0.5 1];
z = 0.01./(repmat(xgrid,[1 4]).*repmat(ygrid,[4 1]));
x1 = 0.05;
y1 = 0.05;
[xmesh,ymesh] = ndgrid(xgrid,ygrid);
interpolant1 = griddedInterpolant(xmesh,ymesh,z,'cubic');
z1 = interpolant1(x1, y1);
for ix = 1:length(xgrid);
interpolant2(ix) = interp1(ygrid, z(ix,:), y1, 'pchip');
end
z1 = interp1(xgrid, interpolant2, x1, 'pchip');
In my example, interpolant1 will use 'spline' instead of 'cubic' and seems less accurate due to overshooting (http://blogs.mathworks.com/cleve/2012/07/16/splines-and-pchips/). However, interpolant1 is much faster.
My main question: what can I do to make my code for interpolant2 more efficient? vectorization? use an alternative to interp1?
Related
I am trying to use the spherical harmonics to represent a perturbation of a spherical object in an acoustic and fluid flow in 3D.
So far, I have been able to use a 2D perturbation on a 3D spherical object, however, I would like to extend that.
The current code represents a 3D sphere with a 2D perturbation, so the perturbation is only in x and y:
clearvars; clc; close all;
Nx = 128;
Ny = 128;
Nz = 128;
Lx =128;
Ly = 128;
Lz = 128;
xi = (0:Nx-1)/Nx*2*pi;
xi_x = 2*pi/Lx;
x = xi/xi_x;
yi = (0:Ny-1)/Ny*2*pi;
yi_y = 2*pi/Ly;
y = yi/yi_y;
zi = (0:Nz-1)/Nz*2*pi;
zi_z = 2*pi/Lz;
z = zi/zi_z;
[X,Y,Z] = meshgrid(x,y,z);
A = 2*pi / Lx;
B = 2*pi / Ly;
C = 2*pi / Lz;
x0 = 64;
y0 = 64;
z0 = 64;
rx0 = 20;
ry0 = 20;
rz0 = 20;
p = 3;
b = 0.1; % pert amplitude
c = 12;
d = 1;
a = 4;
theta = atan2(Y -y0, X-x0) - (pi/c);
p0 = ((X-x0) .* (X-x0)) /(rx0 * rx0) + ((Y-y0) .* (Y-y0))/(ry0 * ry0) + ((Z-z0) .* (Z-z0))/(rz0 * rz0);
Test =d + a * exp((-1. * p0 .* (1 - b .* cos(c * theta))).^p) ;
figure
isosurface(X,Y,Z,Test);
shading flat;
grid on;
Which returns the isosurface:
However, I would like to achieve something similar to this plot (perturbation in z as well):
This is my attempt using spherical harmonics to reproduce the above picture:
clearvars; clc; close all;
%in spherical coord
%calculate r
Nx = 128;
Ny = 128;
Nz = 128;
Lx =128;
Ly = 128;
Lz = 128;
xi = (0:Nx-1)/Nx*2*pi;
xi_x = 2*pi/Lx;
x = xi/xi_x;
yi = (0:Ny-1)/Ny*2*pi;
yi_y = 2*pi/Ly;
y = yi/yi_y;
zi = (0:Nz-1)/Nz*2*pi;
zi_z = 2*pi/Lz;
z = zi/zi_z;
r = sqrt(x.^2 + y.^2 + z.^2);
% Create the grid
delta = pi/127;
%Taking for instance l=1, m=-1 you can generate this harmonic on a (azimuth, elevation) grid like this:
azimuths = 0 : delta : pi;
elevations = 0 : 2*delta : 2*pi;
[R, A, E] = ndgrid(r, azimuths, elevations); %A is phi and E is theta
H = 0.25 * sqrt(3/(2*pi)) .* exp(-1j*A) .* sin(E) .* cos(E);
%transform the grid back to cartesian grid like this:
%can also add some radial distortion to make things look nicer:
%the radial part depends on your domain
X = r .* cos(A) .* sin(E);
Y = r .* sin(A) .* sin(E);
Z = r .* cos(E);
%parameters
x0 = 64;
y0 = 64;
z0 = 64;
rx0 = 20;
ry0 = 20;
rz0 = 20;
p = 3;
b = 0.1; % pert amplitude
%c = 12;
d = 1;
a = 4;
p0 = ((X-x0) .* (X-x0)) /(rx0 * rx0) + ((Y-y0) .* (Y-y0))/(ry0 * ry0) + ((Z-z0) .* (Z-z0))/(rz0 * rz0);
Test1 =d + a * exp((-1. * p0 .*H).^p) ;
figure
isosurface(X,Y,Z,real(Test1)); %ERROR
This gives me the following error:
Error using griddedInterpolant
Grid arrays must have NDGRID structure.
Is the issue in the way I am setting up the spherical harmonics? or the functional form of Test1?? Thanks
I think the problem is that you've created a NDGrid in the second code. In the first code that worked you created a meshgrid.
isosurface expects a grid in meshgrid format and transforms it later into an NDGrid format with permute just for the usage of the griddedInterpolant function. By the input of an NDGrid this operation will fail.
Why did you switch to creating a NDGrid? Did you get the same error when using meshgrid?
EDIT
OK, new theory: I think the problem is the grid itsself. Read the documentation on ndgrid for more information but for short: A NDGrid format is a completely rectangular grid where all nodes are exclusivly surrounded by 90° angles. By spanning up a grid with ndgrid(r, azimuths, elevations) or meshgrid(r, azimuths, elevations) you are getting this rectangular grid but of course this grid is meaningless because r, azimuths and elevations represent spherical coordinates. By later converting R, A and E into the carthesian coordinates X, Y and Z you get a propper spherical grid but the grid isn't a NDGrid structure anymore since it's not rectangular anymore.
So you have to find a workaround to calculate with your spherical grid.
Maybe you can use another function to visualize your data that dosn't take any grid format as input
Or maybe you can try working with a rectangular cartesian grid by setting all values outside the sphere to 0. (You have to refine your grid accordingly to achieve a good estimation of the sphere)
I'm trying to make a plot that can display both wind speed and wind direction over time. A colleague suggested adding a line pointing in the wind direction to each point on a time-series plot of wind speed.
I thought it would be fairly simple to calculate the line. I used trig formulas from here. However the plot is not displaying as I'd expect. All of the lines look as if they have zero slope rather than vary from -1 to 1.
Here is my code:
wdates = [ 7.357325746759259E5; 7.357325747916667E5; 7.357325749074074E5; 7.357325750231481E5; 7.357325751388889E5; 7.357325752546296E5; 7.357325753703704E5; 7.357325754861111E5; 7.357325756018518E5; 7.357325757175926E5; 7.357325758333333E5; 7.357325759490741E5; 7.357325760648148E5];
topspeed = rand(size(wdates)) * 2;
toprdir = [0 pi/6 pi/4 pi/3 pi/2 2*pi/3 3*pi/4 pi 5*pi/4 4*pi/3 3*pi/2 5*pi/3 7*pi/4];
toprdir = toprdir';
h = figure(1);
plot(wdates,topspeed,'s');
datetick('x')
hold all;
%find slope
topslopes = tan(toprdir);
for i=1:length(wdates)
%find start point.
clear x;
clear y;
x(1) = wdates(i);
y(1) = topspeed(i);
d = .0001;
%x(2) = d * cos(atan(topslopes(i))) + x(1); %did not work?
%y(2) = d * sin(atan(topslopes(i))) + y(1); %did not work?
x(2) = d * cos(toprdir(i)) + x(1);
y(2) = d * sin(toprdir(i)) + y(1);
plot(x,y);
end
And here is the result.
You are seeing that all the lines look like they have a slope of zero because your axes have very different ranges.
Instead, create a scaling factor based on your axes ranges. Note that, I just approximated values for dy, dx, but you should calculate what they should be based on your data and physical dimensions of each of your axis (e.g. make sure a 45 deg line looks like 45 degrees). You can use "axis square" to make dimensions the same.
dy = 1;
dx = 0.001;
x(2) = dx * cos(toprdir(i)) + x(1);
y(2) = dy * sin(toprdir(i)) + y(1);
With these lines modified, the resulting graph looks as follows:
I am trying to rotate the derivative of a gaussian (or the original guassian for that matter) by applying a rotation matrix to the X,Y coordinates and then plotting it as a mesh in matlab, but I'm running into an issue that the plot will only rotate by 90 degress each time and for all n*pi points there is no mesh appearing at all. I am wondering what I am doing wrong, hopefully someone can spot my error. I'm fairly new to matlab so forgive me if the code is not pretty. Thank you!
This is the code that I have:
sigma = 4;
[x,y] = deal(-3*sigma:.5:3*sigma);
[X,Y] = meshgrid(x,y);
B = [transpose(x) transpose(y)];
for i=1:1:16
figure(i);
theta = i*pi/8;
rotation = [cos(theta) sin(theta); -sin(theta) cos(theta)];
A = B * rotation;
[x_new, y_new] = meshgrid(A(:,1)', A(:,2)');
mesh(x_new, y_new, dgauss_x(x_new, y_new, sigma));
end
function f = dgauss_x(x, y, sigma)
%first order derivative of Gaussian
f = -x .* gaussian(x, y, sigma) ./ sigma^2;
function f = gaussian(x, y, sigma)
f = exp(-(x .^ 2 + y .^ 2)/(2*sigma^2)) / (sqrt(2*pi*(sigma^2)));
I figure it out. It was a dumb error. Basically I had to create a meshgrid for X and Y, reshape it into an Nx2 matrix of X and Y. Apply the rotation matrix, then reshape back and apply the Gaussian.
The code looks as follows (could be cleaned up):
for i=1:1:16
figure(i);
theta = i*pi/8;
rotation = [cos(theta) -sin(theta); sin(theta) cos(theta)];
X1 = reshape(X, length(x)*length(x), 1);
Y1 = reshape(Y, length(y)*length(y), 1);
B2 = [X1 Y1];
A = B2 * rotation;
X1 = A(:,1);
X1 = reshape(X1, length(x), length(x));
Y1 = A(:,2);
Y1 = reshape(Y1, length(y), length(y));
mesh(X1, Y1, dgauss_x(X1, Y1, sigma));
end
I am trying to write some code to generate a plot similar to the one below on matlab (taken from here):
I have a set of points on a curve (x_i,y_i,z_i). Each point generates a Gaussian distribution (of mean (x_i,y_i,z_i) and covariance matrix I_3).
What I did is I meshed the space into npoint x npoints x npoints and computed the sum of the probability densities for each of the 'sources' (x_i,y_i,z_i) in each point (x,y,z). Then, if the value I get is big enough (say 95% of the maximum density), I keep the point. otherwise I discard it.
The problem with my code is that it is too slow (many for loops) and the graph I get doesn't look like the one below:
Does anyone know whether there is a package to get a similar plot as the one below?
Using isosurface we can do reasonably well. (Although I'm not honestly sure what you want, I think this is close:
% Create a path
points = zeros(10,3);
for ii = 2:10
points(ii, :) = points(ii-1,:) + [0.8 0.04 0] + 0.5 * randn(1,3);
end
% Create the box we're interested in
x = linspace(-10,10);
y = x;
z = x;
[X,Y,Z] = meshgrid(x,y,z);
% Calculate the sum of the probability densities(ish)
V = zeros(size(X));
for ii = 1:10
V = V + 1/(2*pi)^(3/2) * exp(-0.5 * (((X-points(ii,1)).^2 + (Y-points(ii,2)).^2 + (Z-points(ii,3)).^2)));
end
fv = isosurface(X,Y,Z,V, 1e-4 * 1/(2*pi)^(3/2), 'noshare');
fv2 = isosurface(X,Y,Z,V, 1e-5 * 1/(2*pi)^(3/2), 'noshare');
p = patch('vertices', fv.vertices, 'faces', fv.faces);
set(p,'facecolor', 'none', 'edgecolor', 'blue', 'FaceAlpha', 0.05)
hold on;
p2 = patch('vertices', fv2.vertices, 'faces', fv2.faces);
set(p2,'facecolor', 'none', 'edgecolor', 'red', 'FaceAlpha', 0.1)
scatter3(points(:,1), points(:,2), points(:,3));
If I have two plots defined by two different equations:
x = 0:0.01:30;
y1 = x .^2 + 2;
y2 = x .^3 ;
and I plot them as
plot(x, y1, x, y2);
How do I get a small ring around the point of intersection programatically (as in the following plot)?
You'll have to find the point of intersection (px, py) manually:
idx = find(y1 - y2 < eps, 1); %// Index of coordinate in array
px = x(idx);
py = y1(idx);
Remember that we're comparing two numbers in floating point representation, so instead of y1 == y2 we must set a tolerance. I've chosen it as eps, but it's up to you to decide.
To draw a circle around this point, you can compute its points and then plot them, but a better approach would be to plot one point with a blown-up circle marker (credit to Jonas for this suggestion):
plot(px, py, 'ro', 'MarkerSize', 18)
This way the dimensions of the circle are not affected by the axes and the aspect ratio of the plot.
Example
x = 0:0.01:30;
y1 = x .^ 2 + 2;
y2 = x .^ 3;
%// Find point of intersection
idx = find(y1 - y2 < eps, 1);
px = x(idx);
py = y1(idx);
figure
plot(x, y1, x, y2, px, py, 'ro', 'MarkerSize', 18)
axis([0 10 0 10])
This should produce the following plot:
In your example, when you have x, y1 and y2
What you can do is
idx = find(abs(y1 - y2) == min(abs(y1 - y2)));
xInter = x(idx)
yInter = y1(idx) % or y2(idx)
If you have x1, y1 and x2, y2, where x1 ~= x2
you could first do 1D interpolation using
yy2 = interp1(x2, y2, x1);
then apply
idx = find(abs(y1 - yy2) == min(abs(y1 - yy2)));
xInter = x1(idx)
yInter = y1(idx) % or yy2(idx)
Excellent post by #EitanT, however I would like to complement this with a different (automated) way to find the intersection (Assuming there is one and the graphs behave nicely).
Here is our starting point:
x = 0:0.01:30;
y1 = x .^2 + 2;
y2 = x .^3 ;
First of all we check whether these values are exactly equal, for non-floating point non-discrete situations this should be sufficient:
idx = find(y1==y2)
If they are never recorded to be exactly equal, an intersection occurs if one surpasses the other, hence we look at the difference:
if isempty(idx)
d = y1-y2;
% At the moment of crossing, the sign will change:
s = diff(sign(d));
% Now just find the point where it changes
f = find(s,1);
end
To summarize this in compact form without additional variables, I would recommend using:
idx = find(y1==y2)
if isempty(idx)
idx = find(diff(sign(y1-y2)),1)
end
Especially when knowing the functions, the symbolic math toolbox can be used.
y1 = x .^2 + 2;
y2 = x .^3 ;
syms x real
intersection=simplify(solve(y1==y2))
Use vpa(intersection) to convert it to a number or double(intersection) to convert it to a floating point value.
Last but not least, perhaps the cleanest way to do this is the command polyxpoly:
[xi,yi] = polyxpoly(x,y1,x,y2)
xi = 1.69560153754948
yi = 4.87508921229275
Good luck!