Curve defined by the intersection between two scattered point surfaces, sampled differently - python

I have two sets 1__scatter_xyz.dat and 2__scatter_xyz.dat of scattered points.
These points are defined by 3 coordinates: x, y, z
1__scatter_xyz.dat : https://paste.ubuntu.com/25069931/
2__scatter_xyz.dat : https://paste.ubuntu.com/25069938/
These two sets of scattered points intersect in a region:
gnuplot> splot "1__scatter_xyz.dat" using 3:1:2 with points lt 1 title "1", "2__scatter_xyz.dat" using 3:1:2 with points lt 1 lc 2 title "2"
gnuplot> set xlabel 'x'
gnuplot> set ylabel 'y'
gnuplot> set zlabel 'z'
The crossing between the surface of set 1 with the surface of set 2 will define a line / curve, that plotted in a 2D y-xdiagram, will give us the phase boundary between these two sets.
I would like to plot in a 2D y-xdiagram this line / curve that arises from the crossing of both surfaces.
The way I thought on how to attack this problem :
We can define a new function, w = z_{1} - z_{2}.
The crossing between these two surfaces will be the points where w = (z_{1} - z_{2}) = 0.
I could then define two regions:
a) A region where w = 0
b) A region where w \neq 0
If I plot these two values of w in a 2D y-xdiagram :
I could then define that this line / curve is the phase boundary between these two sets:
a) The region where w = 0 is where both sets coexist together
b) The region where w \neq 0 is where both sets do not coexist together
Why I cannot progress with this solution:
If we just remove the blank lines on the .dat files and sort x- wise:
sed '/^\s*$/d' 1__scatter_xyz.dat | grep -v "^#" | sort -k1 -n > 1__scatter_xyz_sort_x_wise.dat
sed '/^\s*$/d' 2__scatter_xyz.dat | grep -v "^#" | sort -k1 -n > 2__scatter_xyz_sort_x_wise.dat
If you look at both x_wise.dat files, there is overlapping data:
set 1 goes from a y of -4.41 to 10.85, and set 2 goes from 8.06 to 17.64. The array of y is different on both sets. However, the array of x is the same: from 10 to 2000 with a step of 20.1.
Thus, set 1 and set 2 have the same array of x_{j}: from 10 to 2000 in a step of 20.1.
However, both sets do not have the same array of ys: there is an array y_{i}^{1} for set 1 and an array y_{i}^{2} for set 2.
In other words,
Thus, imagine that I find a point where both surfaces have the same value of z.
This point will be defined by x_{j}, y_{i}^{1} and y_{i}^{2} instead of two unique coordinates.
More efficient ideas are more than welcome.
Using scipy's griddata for this:
import numpy as np
import sys
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
# Load data:
x_1, y_1, z_1 = = np.loadtxt(./1__scatter_xyz.dat, skiprows = 1).T
x_2, y_2, z_2 = = np.loadtxt(./2__scatter_xyz.dat, skiprows = 1).T
# According to the example posted in the above scipy's griddata link,
# variables "points" and "values" are defined, so we can similarly use:
points_1 = (x_1, y_1)
points_2 = (x_2, y_2)
values_1 = (z_1)
values_2 = (z_2)
We would now have to define the grid.
As explained deeply on the post, the array of y is sampled differently on both sets.
I we carefully study the data, there is a region of overlapping between both sets on the y space:
So, continuing with this scipy's griddata example, we can set:
T_initial = 10.0
T_end = 2000.0
number_of_Ts = 100
P_initial = 8.0622
P_end = 10.8535
number_of_Ps = 100
# And then define the mesh as:
grid_T, grid_P = np.meshgrid(np.linspace(T_initial, T_end, number_of_Ts), np.linspace(P_initial, P_end, number_of_Ps))
At this point I do not know how to continue, because we can actually just define two sets of grids ?
grid_Gibbs_solid_1 = griddata(points_solid_1, values_solid_1, (grid_T, grid_P), method='cubic')
grid_Gibbs_solid_2 = griddata(points_solid_2, values_solid_2, (grid_T, grid_P), method='cubic')
Which would be the approach to follow ?

Let f(x,y) and g(x,y) denote the functions corresponding to your two surfaces. What you are looking for is to plot the contour corresponding to the equation f(x,y) == g(x,y), or, equivalently f(x,y) - g(x,y) == 0.
Matplotlib offers the function contour for this purpose. As a simple example, consider the two surfaces given by the functions
import numpy as np
def f(x, y):
return np.exp(-(x**2 + y**2))
def g(x, y):
return (3*x**2 + y**2)/16
The following snippet plots the function f-g, the (3D) contour corresponding to f-g==0 as well as its (2D) projection on the z-plane
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
fig = plt.figure(figsize = (8,8))
ax = fig.gca(projection='3d')
X = np.linspace(-2, 2, 30)
Y = np.linspace(-2, 2, 30)
X, Y = np.meshgrid(X, Y)
Z = g(X,Y)
ax.plot_surface(X, Y, f(X,Y)-g(X,Y), rstride=1, cstride=1, cmap = cm.viridis, antialiased=False, alpha = 0.5)
ax.contour(X, Y, f(X,Y) - g(X,Y), zdir='z', offset=-2, levels = [0])
ax.contour(X, Y, f(X,Y) - g(X,Y), levels = [0])
ax.set_zlim(zmin = -2)
In your case, you have data samples instead of functions. You may easily obtain (approximate) functions of the surfaces from your data by interpolation (see scipy.interpolate).

Related

Most efficient way to calculate point wise surface normal from a numpy grid

say we have a 2D grid that is projected on a 3D surface, resulting in a 3D numpy array, like the below image. What is the most efficient way to calculate a surface normal for each point of this grid?
I can give you an example with simulated data:
I showed your way, with three points. With three points you can always calculate the cross product to get the perpendicular vector based on the two vectors created from three points. Order does not matter.
I took the liberty to also add the PCA approach using predefined sklearn functions. You can create your own PCA, good exercise to understand what happens under the hood but this works fine. The benefit of the approach is that it is easy to increase the number of neighbors and you are still able to calculate the normal vector. It is also possible to select the neighbors within a range instead of N nearest neighbors.
If you need more explanation about the working of the code please let me know.
from functools import partial
import numpy as np
from sklearn.neighbors import KDTree
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Grab some test data.
X, Y, Z = axes3d.get_test_data(0.25)
X, Y, Z = map(lambda x: x.flatten(), [X, Y, Z])
plt.plot(X, Y, Z, '.')
plt.show(block=False)
data = np.array([X, Y, Z]).T
tree = KDTree(data, metric='minkowski') # minkowki is p2 (euclidean)
# Get indices and distances:
dist, ind = tree.query(data, k=3) #k=3 points including itself
def calc_cross(p1, p2, p3):
v1 = p2 - p1
v2 = p3 - p1
v3 = np.cross(v1, v2)
return v3 / np.linalg.norm(v3)
def PCA_unit_vector(array, pca=PCA(n_components=3)):
pca.fit(array)
eigenvalues = pca.explained_variance_
return pca.components_[ np.argmin(eigenvalues) ]
combinations = data[ind]
normals = list(map(lambda x: calc_cross(*x), combinations))
# lazy with map
normals2 = list(map(PCA_unit_vector, combinations))
## NEW ##
def calc_angle_with_xy(vectors):
'''
Assuming unit vectors!
'''
l = np.sum(vectors[:,:2]**2, axis=1) ** 0.5
return np.arctan2(vectors[:, 2], l)
dist, ind = tree.query(data, k=5) #k=3 points including itself
combinations = data[ind]
# map with functools
pca = PCA(n_components=3)
normals3 = list(map(partial(PCA_unit_vector, pca=pca), combinations))
print( combinations[10] )
print(normals3[10])
n = np.array(normals3)
n[calc_angle_with_xy(n) < 0] *= -1
def set_axes_equal(ax):
'''Make axes of 3D plot have equal scale so that spheres appear as spheres,
cubes as cubes, etc.. This is one possible solution to Matplotlib's
ax.set_aspect('equal') and ax.axis('equal') not working for 3D.
Input
ax: a matplotlib axis, e.g., as output from plt.gca().
FROM: https://stackoverflow.com/questions/13685386/matplotlib-equal-unit-length-with-equal-aspect-ratio-z-axis-is-not-equal-to
'''
x_limits = ax.get_xlim3d()
y_limits = ax.get_ylim3d()
z_limits = ax.get_zlim3d()
x_range = abs(x_limits[1] - x_limits[0])
x_middle = np.mean(x_limits)
y_range = abs(y_limits[1] - y_limits[0])
y_middle = np.mean(y_limits)
z_range = abs(z_limits[1] - z_limits[0])
z_middle = np.mean(z_limits)
# The plot bounding box is a sphere in the sense of the infinity
# norm, hence I call half the max range the plot radius.
plot_radius = 0.5*max([x_range, y_range, z_range])
ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])
ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])
ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])
u, v, w = n.T
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
# ax.set_aspect('equal')
# Make the grid
ax.quiver(X, Y, Z, u, v, w, length=10, normalize=True)
set_axes_equal(ax)
plt.show()
The surface normal for a point cloud is not well defined. One way to define them is from the surface normal of a reconstructed mesh using triangulation (which can introduce artefacts regarding you specific input). A relatively simple and fast solution is to use VTK to do that, and more specifically, vtkSurfaceReconstructionFilter and vtkPolyDataNormals . Regarding your needs, it might be useful to apply other filters.

Regular Distribution of Points in the Volume of a Sphere

I'm trying to generate a regular n number of points within the volume of a sphere. I found this similar answer (https://scicomp.stackexchange.com/questions/29959/uniform-dots-distribution-in-a-sphere) on generating a uniform regular n number of points on the surface of a sphere, with the following code:
import numpy as np
n = 5000
r = 1
z = []
y = []
x = []
alpha = 4.0*np.pi*r*r/n
d = np.sqrt(alpha)
m_nu = int(np.round(np.pi/d))
d_nu = np.pi/m_nu
d_phi = alpha/d_nu
count = 0
for m in range (0,m_nu):
nu = np.pi*(m+0.5)/m_nu
m_phi = int(np.round(2*np.pi*np.sin(nu)/d_phi))
for n in range (0,m_phi):
phi = 2*np.pi*n/m_phi
xp = r*np.sin(nu)*np.cos(phi)
yp = r*np.sin(nu)*np.sin(phi)
zp = r*np.cos(nu)
x.append(xp)
y.append(yp)
z.append(zp)
count = count +1
which works as intended:
How can I modify this to generate a regular set of n points in the volume of a sphere?
Another method to do this, yielding uniformity in volume:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
dim_len = 30
spacing = 2 / dim_len
point_cloud = np.mgrid[-1:1:spacing, -1:1:spacing, -1:1:spacing].reshape(3, -1).T
point_radius = np.linalg.norm(point_cloud, axis=1)
sphere_radius = 0.5
in_points = point_radius < sphere_radius
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(point_cloud[in_points, 0], point_cloud[in_points, 1], point_cloud[in_points, 2], )
plt.show()
Output (matplotlib mixes up the view but it is a uniformly sampled sphere (in volume))
Uniform sampling, then checking if points are in the sphere or not by their radius.
Uniform sampling reference [see this answer's edit history for naiive sampling].
This method has the drawback of generating redundant points which are then discarded.
It has the upside of vectorization, which probably makes up for the drawback. I didn't check.
With fancy indexing, one could generate the same points as this method without generating redundant points, but I doubt it can be easily (or at all) vectorized.
Sample uniformly along X. For every value of X, you draw two Y from X²+Y²=1. Sample uniformly between these two Y. Then for every (X, Y) pair, you draw two Z from X²+Y²+Z²=1. Sample uniformly between these two Z.

Sorting Data for Matplotlib Surface Plot

I’m trying to plot Riemann surface (imaginary part, real part is color-coded) for a complex valued function
using matplolib.
As it follows from nature of complex multivalued function, when some path passes a branch cut
its image jumps on f(z) plane.
As you can see in the code below, my mesh consists of circles
in polar coordinate system, which are the paths, passing the branch cut. It results in a jump that you can see in the figure below.
Question:
Is there any way to stitch the parts of the plot in a proper way.
To me a possible solution looks like either:
generate the values of mesh, bounded by the branch cut, and then concatenate the parts of them somehow (maybe even matplotlib itself has a function for it), or
sort out z-axis values being obtained on a simple mesh, which I use in the code
I've tried both but not succeeded.
If anybody has already faced such issues, or has any ideas, I would appreciate your comments!
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
k0 = 3 - 0.5j
# polar coordinates r and phi
Uphi = np.arange(0,2*np.pi,0.002) # phi from 0 to 2pi
Vr = np.arange(0,5,0.02) # r from 0 to 5
# polar grid
U, V = np.meshgrid(Uphi, Vr)
# coordinates on conplex plane: Z = X + iY; X,Y - cartezian coords
X = V*np.cos(U)
Y = V*np.sin(U)
Z1 = np.imag(-np.sqrt(-k0**2 + (X + 1j*Y)**2)) # first branch of the multivalued function
Z2 = np.imag(+np.sqrt(-k0**2 + (X + 1j*Y)**2)) # second branch
Z = np.zeros((Z1.shape[0],2*Z1.shape[1])) # resultng array to plot
Z[:,:Z1.shape[1]] = Z1
Z[:,Z1.shape[1]:] = Z2
# colormap -- color-coding the imaginary part of the 4D function -- another dimension
Z_cv = np.zeros((Z1.shape[0],2*Z1.shape[1]))
Z_cv[:,:Z1.shape[1]] = np.real(-np.sqrt(-k0**2 + (X + 1j*Y)**2))
Z_cv[:,Z1.shape[1]:] = np.real(+np.sqrt(-k0**2 + (X + 1j*Y)**2))
# expanding grid for two branches Z1 and Z2
X1 = np.zeros((Z1.shape[0],2*Z1.shape[1]))
Y1 = np.zeros((Z1.shape[0],2*Z1.shape[1]))
X1[:,:Z1.shape[1]] = X
X1[:,Z1.shape[1]:] = X
Y1[:,:Z1.shape[1]] = Y
Y1[:,Z1.shape[1]:] = Y
# plotting the surface
F = plt.figure(figsize=(12,8),dpi=100)
A = F.gca(projection='3d',autoscale_on=True)
# normalizing the values of color map
CV = (Z_cv + np.abs(np.min(Z_cv)))/(np.max(Z_cv) + np.abs(np.min(Z_cv)))
A.plot_surface(X1, Y1, Z, rcount=100, ccount=200, facecolors=cm.jet(CV,alpha=.3))
A.view_init(40,70)
m = cm.ScalarMappable(cmap=cm.jet)
m.set_array(Z_cv)
plt.colorbar(m)
plt.show()
The following figures represent two regular branches of the function on the Riemann surface using using Domain coloring approach. It more clearly shows those jumps.
import cplot
import numpy as np
cplot.show(lambda z: np.sqrt(z**2 - k0**2), -5, +5, -5, +5, 100, 100,alpha=0.7)
cplot.show(lambda z: -np.sqrt(z**2 - k0**2), -5, +5, -5, +5, 100, 100,alpha=0.7)

Plot Surface instead of parametric curve

I am working on using the forward difference scheme for numerically solving the diffusion function in one dimension. My final plot of the solution should be a surface where the solution u(x,t) is plotted over a grid of x and t values. I have the problem solved, but I can't get the data to be plotted with the grid representation.
I can think of 2 ways to fix this:
1.) My x and t arrays should be one dimensional, but my u array should be a 2D array. Ultimately, I want a square matrix for u, but I am having a hard time coding that. Currently I have a 1D array for u. Here is the code where u is populated.
u = zeros(Nx+1) # unknown u at new time level
u_1 = zeros(Nx+1) # u at the previous time level
# Set initial condition u(x,0) = I(x)
for i in range(0, Nx+1):
#set initial u's to I(xi)
u_1[i] = 25-x[i]**2
for n in range(0, Nt):
# Compute u at inner mesh points
for i in range(1, Nx):
u[i] = u_1[i] + F*(u_1[i-1] - 2*u_1[i] + u_1[i+1])
2.) The above code returns a 1D array for u, is there a way to plot a 3D surface with 3 1D arrays for x,y,z?
Well..., there is a lot of information you haven't provided. For instance you said you wanted a x,y,z plot but haven't said what the x, y and z should be in the context of your plot. Also z is typically z(x,y).
The following recipe assumes a t and x, and u(t,x) as variables to be put into a surface. I imagine is not exactly your idea but it should be adaptable to your exercise:
EDIT: Also your code (which is in the function computeU in this recipe) had a loop for Nt that does not seem to do anything. I've removed it for the purpose of this example.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
def computeU(Nx,x,F,Nt):
u = np.zeros(Nx+1) # unknown u at new time level
u_1 = np.zeros(Nx+1) # u at the previous time level
# Set initial condition u(x,0) = I(x)
for i in range(0, Nx+1):
#set initial u's to I(xi)
u_1[i] = 25-x[i]**2
#for n in range(0, Nt): # I'm not sure what this is doing. It has no effect.
# Compute u at inner mesh points
for i in range(1, Nx):
u[i] = u_1[i] + F*(u_1[i-1] - 2*u_1[i] + u_1[i+1])
return np.hstack((u[:,np.newaxis],u_1[:,np.newaxis]))
Nx = 10
F = 3
Nt = 5
x = np.arange(11)
t = np.arange(2)
X,Y = np.meshgrid(t,x)
Z = computeU(Nx,x,F,Nt)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,linewidth=0, antialiased=False)
plt.show()
Notice how I've used meshgrid to build new t,x (from 1D arrays) to be mapped against your stack of U arrays (which will have the same shape of X,Y - the new t,x). The result is this:

Discretize path with numpy array and equal distance between points

Lets say I have a path in a 2d-plane given by a parametrization, for example the archimedian spiral:
x(t) = a*φ*cos(φ), y(t) = a*φ*sin(φ)
Im looking for a way to discretize this with a numpy array,
the problem is if I use
a = 1
phi = np.arange(0, 10*np.pi, 0.1)
x = a*phi*np.cos(phi)
y = a*phi*np.sin(phi)
plt.plot(x,y, "ro")
I get a nice curve but the points don't have the same distance, for
growing φ the distance between 2 points gets larger.
Im looking for a nice and if possible fast way to do this.
It might be possible to get the exact analytical formula for your simple spiral, but I am not in the mood to do that and this might not be possible in a more general case. Instead, here is a numerical solution:
import matplotlib.pyplot as plt
import numpy as np
a = 1
phi = np.arange(0, 10*np.pi, 0.1)
x = a*phi*np.cos(phi)
y = a*phi*np.sin(phi)
dr = (np.diff(x)**2 + np.diff(y)**2)**.5 # segment lengths
r = np.zeros_like(x)
r[1:] = np.cumsum(dr) # integrate path
r_int = np.linspace(0, r.max(), 200) # regular spaced path
x_int = np.interp(r_int, r, x) # interpolate
y_int = np.interp(r_int, r, y)
plt.subplot(1,2,1)
plt.plot(x, y, 'o-')
plt.title('Original')
plt.axis([-32,32,-32,32])
plt.subplot(1,2,2)
plt.plot(x_int, y_int, 'o-')
plt.title('Interpolated')
plt.axis([-32,32,-32,32])
plt.show()
It calculates the length of all the individual segments, integrates the total path with cumsum and finally interpolates to get a regular spaced path. You might have to play with your step-size in phi, if it is too large you will see that the spiral is not a smooth curve, but instead built from straight line segments. Result:

Categories