I am trying to triangulate an annulus using the scipy.spatial.Delaunay() function, but cannot get the desired result. Here is my code:
from scipy.spatial import Delaunay
NTheta = 26
NR = 8
a0 = 1.0
#define base rectangle (r,theta) = (u,v)
u=np.linspace(0, 2*np.pi, NTheta)
v=np.linspace(1*a0, 3*a0, NR)
u,v=np.meshgrid(u,v)
u=u.flatten()
v=v.flatten()
#evaluate the parameterization at the flattened u and v
x=v*np.cos(u)
y=v*np.sin(u)
#define 2D points, as input data for the Delaunay triangulation of U
points2D=np.vstack([u,v]).T
xy0 = np.vstack([x,y]).T
Tri1 = Delaunay(points2D) #triangulate the rectangle U
Tri2 = Delaunay(xy0) #triangulate the annulus
#plt.scatter(x, y)
plt.triplot(x, y, Tri1.simplices, linewidth=0.5)
plt.show()
plt.triplot(x, y, Tri2.simplices, linewidth=0.5)
plt.show()
I get the following:
The triangulation of the annulus itself clearly gives unwanted triangles. The triangulation of the base rectangle seems to give the proper result, until you realise that the annulus is not actually closed, by stretching the annulus (i.e., moving its nodes) a bit.
So, my question is, how do I get the proper triangulation that accounts for the non-trivial topology? Can I remove simplices from the triangulation of the annulus -- for example, based on the length of the bonds -- or somehow stitch the two ends of the base rectangle together? Is there a simple way of doing this?
Answer:
I accepted the answer below but it does not completely solve the question as asked. I still don't know how to tile a periodic surface using scipy.Delaunay (i.e., the qhull routine). However, using a mask as defined below, one can create a new list of triangle simplices, and that should serve for many purposes. However, one cannot use this list with the other methods defined in the scipy.Delaunay class. So, be careful!
qhull works with the convex hull. So it can't work directly with that concave interior. In fig2 it is filling the interior with triangles. That may be more obvious if we add a (0,0) point to xy0.
last_pt = xy0.shape[0]
xy1 = np.vstack((xy0,(0,0))) # add ctr point
Tri3 = Delaunay(xy1)
print(Tri3.points.shape, Tri3.simplices.shape)
plt.triplot(Tri3.points[:,0], Tri3.points[:,1], Tri3.simplices, linewidth=0.5)
plt.show()
Remove the simplices that contain that center point:
mask = ~(Tri3.simplices==last_pt).any(axis=1)
plt.triplot(Tri3.points[:,0], Tri3.points[:,1], Tri3.simplices[mask,:], linewidth=0.5)
plt.show()
To stitch the two ends together, removing a value from u seems to work:
u = u[:-1]
In a FEM model you might leave the center elements in place, but give them the appropriate 'neutral' properties (insulating or whatever works).
Related
I'm using a Delaunay triangulation to interpolate in some function values evaluated at a set of parameters on a regular 4-dimensional grid. Sometimes, when a parameter value changes by a small amount that takes it to a new simplex, more than one point in the simplex changes. I expect that as I vary one of the parameter continuously, I'd move from simplex to simplex by changing just one point in the simplex at a time (and that's usually the case in my code, too). Instead, consider this script:
import numpy as np
from scipy.spatial import Delaunay
# hideous construction to get the desired 4d grid of points
# with points at [-1, -0.5, 0, 0.5, 1] along each axis
X = np.vstack(list(map(np.ravel, np.meshgrid(*[np.linspace(-1, 1, 5) for i in range(4)])))).T
tri = Delaunay(X)
delta = 1e-10
print(np.sort(tri.vertices[tri.find_simplex([-0.25, -0.05, 0.5+delta, 0.1])]))
print(np.sort(tri.vertices[tri.find_simplex([-0.25, -0.05, 0.5-delta, 0.1])]))
which produces
[192 292 317 318 322]
[167 292 293 313 317]
Note that these two simplices differ by 3 points, where I expect one, and I haven't devised a 2- or 3-D example where more than one vertex would change.
I'm 99% sure this is because my points are on a regular grid but I can't find a detailed answer of why or how to avoid the problem. I know that the triangulation isn't unique but that isn't fundamentally a problem. Various tricks appear to change where I encounter this issue but I haven't yet found a "fix" that prevents the issue from appearing anywhere.
Edit
I've managed to find an example in 3D, which allows me to visualise the problem.
import numpy as np
from scipy.spatial import Delaunay
X = np.vstack(list(map(np.ravel, np.meshgrid(*[np.linspace(-1, 1, 5) for i in range(3)])))).T
tri = Delaunay(X)
delta = 1e-6
x = np.array([-0.25, 0, 0.07])
fig = pl.figure()
ax = fig.add_subplot(projection='3d')
ax.scatter(*x, c='k')
x[1] = delta
s = X[tri.vertices[tri.find_simplex(x)]]
for i, si in enumerate(s):
for j, sj in enumerate(s[i:]):
ax.plot3D(*np.vstack([si, sj]).T, c='C0')
x[1] = -delta
s = X[tri.vertices[tri.find_simplex(x)]]
for i, si in enumerate(s):
for j, sj in enumerate(s[i:]):
ax.plot3D(*np.vstack([si, sj]).T, c='C1')
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_zlabel('$z$')
and here are two aspects on the output.
The blue and orange simplices are contain the interpolation point before and after it crosses y=0 (from positive to negative). My assumption was that both simplices would have the same triangular face along the y=0 plane but apparently this is incorrect. Is this fundamental to the Delaunay triangulation in this degenerate case or something about the implementation? And is there a way to avoid it? The QHull option Qx (which is in SciPy's default options for D>4) seems to help for this example but I'm not sure about globally.
Your question is not really about the implementation of triangulation in scipy.spatial. and it is more about the math of the Delaunay triangulation as a mathematical object.
Delaunay triangulations in dimension D are very well defined, ... when the points are "general position". That means that no D+2 points from the input points are on a common sphere. If that happens, one says the Delaunay triangulation is "degenerate". When the triangulation is degenerate, the Delaunay triangulation is not well defined, and there exists multiple ways to triangulate the convex hull of the points while preserving the Delaunay property.
What is what you observe: your points are on a regular grid, and that is a very degenerated point set (for the Delaunay property). Any slight modification of the coordinates can trigger the flip of multiple simplices, to restore the Delaunay property.
Maybe you can understand that behavior by having a look at the dual object of the Delaunay triangulation: its Voronoi diagram. For points sets close to a regular grid, the diagram is degenerated: it has Voronoi edges that are zero-length, or with a length close to zero. And any small modification of the coordinates of the point can change the topology of the Voronoi diagram (and thus of the Delaunay triangulation as well).
r = np.linspace(0.1,1,11)
theta = np.linspace(-alpha,alpha,11)
radius_matrix, theta_matrix = np.meshgrid(r,theta)
u_radial = -q*(1/radius_matrix)*u_sol[0]
u_theta = theta_matrix*[0 for x in range(len(u_sol[0]))]
ax = plt.subplot(111, polar=True)
ax.plot(theta_matrix, radius_matrix, u_radial, u_theta) #color='r',
ls='none', marker='.'
plt.show()
I am trying to make a plot of a velocity field (same as vector field) using numpys quiver function. The velocity field is written
where q is just an arbitrary constant and r is the distance to the origin. Now, to plot this in a polar coordinate system I create two meshgrids radius_matrix and theta_matrix, as seen in my code (line three). Together these meshgrids form a polar coordinate plane, with r on the horizontal axis and theta on the vertical axis (at least I think) and each point should have a vector arrow corresponding to the equation above.
So for that to happen I define u_radial and u_theta, which are the vector components in radial and angluar direction, resp.. The variable u_sol[0] contains f(theta) (as seen in the equation) for 11 different theta points, and I thought that this would give the correct vectorcomponent, but it doesnt. Why not?
I am expecting something like this, that the arrow shrinks when I get close to the edge for a single value of r. I just want this but for more values of r. This is the data of my u_sol[0] vector:
u_sol[0] = [4.68520269e-26 1.54380741e+00 2.74550730e+00 3.60503630e+00
4.12217780e+00 4.29651250e+00 4.12741184e+00 3.61407419e+00
2.75560427e+00 1.55113610e+00 3.84028608e-18]
When I plot this, I get something worse, see the figure below. What happend to the arrows? And why are there colors all of a sudden?
Best regards SimpleP.
Is there any decent Pythonic way to interpolate in a triangular mesh, or would I need to implement that myself? That is to say, given a (X,Y) point we'll call P, and a mesh (vertices at (X,Y) with value Z, forming triangular facets), estimate the value at P. So that means first find the facet that contains the point, then interpolate - ideally a higher order interpolation than just "linearly between the facet's vertices" (i.e., taking into account the neighboring facets)?
I could implement it myself, but if there's already something available in Python....
(I checked scipy.interpolate, but its "meshes" seem to just be regular point grids. This isn't a grid, it's a true 2D mesh; the vertices can be located anywhere.)
I often use matplotlib.tri for this purpose. Here Xv,Yv are the vertices (or nodes) of the triangles, and Zv the values at those nodes:
from matplotlib.tri import Triangulation, LinearTriInterpolator, CubicTriInterpolator
#you can add keyword triangles here if you have the triangle array, size [Ntri,3]
triObj = Triangulation(Xv,Yv)
#linear interpolation
fz = LinearTriInterpolator(triObj,Zv)
Z = fz(X,Y)
#cubic interpolation
fzc = CubicTriInterpolator(triObj,Zv)
Zc = fz(X,Y)
I made a triangulation object in matplotlib (out of the P matrix, which contains points coordinates, and the T matrix, which contains the triangles nodes, that describe a rectangle minus a hole) and computed some scalar field called phi at the nodes of this triangulation (using a finite element method applied to a Poisson equation). Using this triangulation I compute the gradient, which is what I am interested in. I made a quiver plot of this vector field, everything is nice, the vectors don't intersect with the hole.
However, when I want to plot streamlines via the streamplot function, they intersect the hole, like in the following figure:
The thing is, in order to trace streamlines, I have to create a structured grid for the streamplot function. But since my vector field is not defined at the nodes of the regular grid, I need to interpolate the values at these nodes. For that I used griddata. The problem is, in doing so, the hole is covered by the regular grid so the vector field becomes defined inside the hole, hence the result. Here is the relevant piece of code that produced the picture:
def plot_streamlines(P, T, phi):
triangulation = tr.Triangulation(P[:,0], P[:,1], T)
interpolator = tr.CubicTriInterpolator(triangulation, phi)
(u_x,u_y) = interpolator.gradient(triangulation.x, triangulation.y)
grid_x, grid_y = np.mgrid[x_min:x_max:100j, y_min:y_max:100j]
grid_u_x = ip.griddata(P, u_x, (grid_x,grid_y), method='cubic')
grid_u_y = ip.griddata(P, u_y, (grid_x,grid_y), method='cubic')
pl.streamplot(grid_x[:,0], grid_y[0,:], -grid_u_x.T, -grid_u_y.T)
I am aware of masked arrays but didn't manage to use the mask to get the result I wanted. I wanted to create a kind of masked regulard grid and then interpolate the vector field on it, but I didn't manage to do it. Does somebody has experience with this kind of problem? Any suggestion will be appreciated.
Thanks!
In your code sample interpolator.gradientis already an interpolator i.e. you do not need to use it in combination with griddata. Try:
grid_u_x, grid_u_y = interpolator.gradient(grid_x, grid_y)
As this interpolator is aware of your triangulation mesh, it should result is a velocity vector filled of nan outside your mesh. Streamplot should hopefully be able to handle this gracefully as in this example:
http://matplotlib.org/examples/images_contours_and_fields/streamplot_demo_masking.html
I have a series of x,y coordinates and associated heading angles for multiple aircraft. I can plot the paths flown, and I would like to use a special marker to mark a particular location along the path that also shows the aircraft's heading when it was at that location.
Using matplotlib.pyplot I've used an arrowhead with no base to do this, but having to define the head and tail locations ended up with inconsistent arrowhead lengths when plotting multiple aircraft. I also used a custom three-sided symbol with the tuple (numsides, style, angle) as well as the wedge and bigvee symbols, but they never look very good.
From Custom arrow style for matplotlib, pyplot.annotate Saullo Castro showed a nice custom arrow (arrow1) that I'm wondering whether it can be used or converted in such a way as to just simply plot it at a given x,y and have its orientation defined by a heading angle.
I can plot the custom arrow with the following. Any ideas on how to rotate it to reflect a heading?
a1 = np.array([[0,0],[0,1],[-1,2],[3,0],[-1,-2],[0,-1],[0,0]], dtype=float)
polB = patches.Polygon(a1, closed=True, facecolor='grey')
ax.add_patch(polB)
Thanks in advance.
So I made the polygon a little simpler and also found that the rotation could be done by using mpl.transforms.Affine2D().rotate_deg_around():
a2 = np.array([[newX,newY+2],[newX+1,newY-1],[newX,newY],[newX-1,newY-1],[newX,newY+2]], dtype=float)
polB = patches.Polygon(a2, closed=True, facecolor='gold')
t2 = mpl.transforms.Affine2D().rotate_deg_around(newX,newY,heading) + newax.transData
polB.set_transform(t2)
newax.add_patch(polB)
I first tried to overlay the polygon on a line plotted from the x,y coordinates. However, the scales of the x and y axes were not equal (nor did I want them to be), so the polygon ended up looking all warped and stretched when rotated. I got around this by first adding a new axis with equal x/y scaling:
newax = fig.add_axes(ax.get_position(), frameon=False)
newax.set_xlim(-20,20)
newax.set_ylim(-20,20)
I could at least then rotate all I wanted and not have the warp issue. But then I needed to figure out how to basically connect the two axes so that I could plot the polygon on the new axis at a point referenced from the original axis. The way I figured to do this was by using transformations to go from the data coordinates on the original axis, converting them to display coordinates, and then inverting them back to data coordinates except this time at the data coordinates on the new axis:
inTrans = ax.transData.transform((x, y))
inv = newax.transData.inverted()
newTrans = inv.transform((inTrans[0], inTrans[1]))
newX = newTrans[0]
newY = newTrans[1]
It felt a little like some sort of Rube Goldberg machine to do it this way, but it did what I wanted.
In the end, I decided I didn't like this approach and went with keeping it simpler and using a fancy arrowhead instead of a polygon. Such is life...