Streamlines on a triangulation with matplotlib - python

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

Related

SciPy Delaunay triangulation changes multiple points of simplex for tiny change in parameters

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).

Getting a proper Delaunay triangulation of an annulus (using python)

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).

Getting a good interpolation/fit for 1d curve in 3d space -- Python

I have a set of 3d coordinates (x,y,z) to which I would like to fit a space curve. Does anyone know of existing routines for this in Python?
From what I have found (https://docs.scipy.org/doc/scipy/reference/interpolate.html), there are existing modules for fitting a curve to a set of 2d coordinates, and others for fitting a surface to a set of 3d coordinates. I want the middle path - fitting a curve to a set of 3d coordinates.
EDIT --
I found an explicit answer to this on another post here, using interpolate.splprep() and interpolate.splenv(). Here are my data points:
import numpy as np
data = np.array([[21.735556483642707, 7.9999120559310359, -0.7043281314370935],
[21.009401429607784, 8.0101161320825103, -0.16388503829177037],
[20.199370045383134, 8.0361339131845497, 0.25664085801558179],
[19.318149385194054, 8.0540100864979447, 0.50434139043379278],
[18.405497793567243, 8.0621753888918484, 0.57169888018720161],
[17.952649703401562, 8.8413995204241491, 0.39316793526155014],
[17.539007529982641, 9.6245700151356104, 0.14326173861202204],
[17.100154581079089, 10.416295524018977, 0.011339000091976647],
[16.645143439968102, 11.208477191735446, 0.070252116425261066],
[16.198247656768263, 11.967005154933993, 0.31087815045809558],
[16.661378578010989, 12.717314230004659, 0.54140549139204996],
[17.126106263351478, 13.503461982612732, 0.57743407626794219],
[17.564249250974573, 14.28890107482801, 0.42307198199366186],
[17.968265052275274, 15.031985807202176, 0.10156997950061938]])
Here is my code:
from scipy import interpolate
from mpl_toolkits.mplot3d import Axes3D
data = data.transpose()
#now we get all the knots and info about the interpolated spline
tck, u= interpolate.splprep(data, k=5)
#here we generate the new interpolated dataset,
#increase the resolution by increasing the spacing, 500 in this example
new = interpolate.splev(np.linspace(0,1,500), tck, der=0)
#now lets plot it!
fig = plt.figure()
ax = Axes3D(fig)
ax.plot(data[0], data[1], data[2], label='originalpoints', lw =2, c='Dodgerblue')
ax.plot(new[0], new[1], new[2], label='fit', lw =2, c='red')
ax.legend()
plt.savefig('junk.png')
plt.show()
This is the image:
You can see that the fit is not good, while I am already using the maximum allowed fitting order value (k=5). Is this because the curve is not fully convex? Does anyone know how I can improve the fit?
Depends on what the points represent, but if it's just position data, you could use a kalman filter such as this one written in python. You could just query the kalman filter at any time to get the "expected point" at that time, so it would work just like a function of time.
If you do plan to use a kalman filter, just set the initial estimate to your first coordinate, and set your covariance to be a diagonal matrix of huge numbers, this will indicate that you are very uncertain about the position of your next point, which will quickly lock the filter onto your coordinates.
You'd want to stay away from spline fitting methods, because splines will always go through your data.
You can fit a curve to any dimensional data. The curve fitting / optimization algorithms (say, in scipy.optimize) all treat the observations you want to model as a plain 1-d array, and do not care what the independent variables are. If you flatten your 3d data, each value will correspond to an (x, y, z) tuple. You can just pass that information along as "extra" data to you fitting routine to help you calculate the model curve that will be fitted to your data.

Python: interpolating in a triangular mesh

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)

Interpolation over an irregular grid

So, I have three numpy arrays which store latitude, longitude, and some property value on a grid -- that is, I have LAT(y,x), LON(y,x), and, say temperature T(y,x), for some limits of x and y. The grid isn't necessarily regular -- in fact, it's tripolar.
I then want to interpolate these property (temperature) values onto a bunch of different lat/lon points (stored as lat1(t), lon1(t), for about 10,000 t...) which do not fall on the actual grid points. I've tried matplotlib.mlab.griddata, but that takes far too long (it's not really designed for what I'm doing, after all). I've also tried scipy.interpolate.interp2d, but I get a MemoryError (my grids are about 400x400).
Is there any sort of slick, preferably fast way of doing this? I can't help but think the answer is something obvious... Thanks!!
Try the combination of inverse-distance weighting and
scipy.spatial.KDTree
described in SO
inverse-distance-weighted-idw-interpolation-with-python.
Kd-trees
work nicely in 2d 3d ..., inverse-distance weighting is smooth and local,
and the k= number of nearest neighbours can be varied to tradeoff speed / accuracy.
There is a nice inverse distance example by Roger Veciana i Rovira along with some code using GDAL to write to geotiff if you're into that.
This is of coarse to a regular grid, but assuming you project the data first to a pixel grid with pyproj or something, all the while being careful what projection is used for your data.
A copy of his algorithm and example script:
from math import pow
from math import sqrt
import numpy as np
import matplotlib.pyplot as plt
def pointValue(x,y,power,smoothing,xv,yv,values):
nominator=0
denominator=0
for i in range(0,len(values)):
dist = sqrt((x-xv[i])*(x-xv[i])+(y-yv[i])*(y-yv[i])+smoothing*smoothing);
#If the point is really close to one of the data points, return the data point value to avoid singularities
if(dist<0.0000000001):
return values[i]
nominator=nominator+(values[i]/pow(dist,power))
denominator=denominator+(1/pow(dist,power))
#Return NODATA if the denominator is zero
if denominator > 0:
value = nominator/denominator
else:
value = -9999
return value
def invDist(xv,yv,values,xsize=100,ysize=100,power=2,smoothing=0):
valuesGrid = np.zeros((ysize,xsize))
for x in range(0,xsize):
for y in range(0,ysize):
valuesGrid[y][x] = pointValue(x,y,power,smoothing,xv,yv,values)
return valuesGrid
if __name__ == "__main__":
power=1
smoothing=20
#Creating some data, with each coodinate and the values stored in separated lists
xv = [10,60,40,70,10,50,20,70,30,60]
yv = [10,20,30,30,40,50,60,70,80,90]
values = [1,2,2,3,4,6,7,7,8,10]
#Creating the output grid (100x100, in the example)
ti = np.linspace(0, 100, 100)
XI, YI = np.meshgrid(ti, ti)
#Creating the interpolation function and populating the output matrix value
ZI = invDist(xv,yv,values,100,100,power,smoothing)
# Plotting the result
n = plt.normalize(0.0, 100.0)
plt.subplot(1, 1, 1)
plt.pcolor(XI, YI, ZI)
plt.scatter(xv, yv, 100, values)
plt.title('Inv dist interpolation - power: ' + str(power) + ' smoothing: ' + str(smoothing))
plt.xlim(0, 100)
plt.ylim(0, 100)
plt.colorbar()
plt.show()
There's a bunch of options here, which one is best will depend on your data...
However I don't know of an out-of-the-box solution for you
You say your input data is from tripolar data. There are three main cases for how this data could be structured.
Sampled from a 3d grid in tripolar space, projected back to 2d LAT, LON data.
Sampled from a 2d grid in tripolar space, projected into 2d LAT LON data.
Unstructured data in tripolar space projected into 2d LAT LON data
The easiest of these is 2. Instead of interpolating in LAT LON space, "just" transform your point back into the source space and interpolate there.
Another option that works for 1 and 2 is to search for the cells that maps from tripolar space to cover your sample point. (You can use a BSP or grid type structure to speed up this search) Pick one of the cells, and interpolate inside it.
Finally there's a heap of unstructured interpolation options .. but they tend to be slow.
A personal favourite of mine is to use a linear interpolation of the nearest N points, finding those N points can again be done with gridding or a BSP. Another good option is to Delauney triangulate the unstructured points and interpolate on the resulting triangular mesh.
Personally if my mesh was case 1, I'd use an unstructured strategy as I'd be worried about having to handle searching through cells with overlapping projections. Choosing the "right" cell would be difficult.
I suggest you taking a look at GRASS (an open source GIS package) interpolation features (http://grass.ibiblio.org/gdp/html_grass62/v.surf.bspline.html). It's not in python but you can reimplement it or interface with C code.
Am I right in thinking your data grids look something like this (red is the old data, blue is the new interpolated data)?
alt text http://www.geekops.co.uk/photos/0000-00-02%20%28Forum%20images%29/DataSeparation.png
This might be a slightly brute-force-ish approach, but what about rendering your existing data as a bitmap (opengl will do simple interpolation of colours for you with the right options configured and you could render the data as triangles which should be fairly fast). You could then sample pixels at the locations of the new points.
Alternatively, you could sort your first set of points spatially and then find the closest old points surrounding your new point and interpolate based on the distances to those points.
There is a FORTRAN library called BIVAR, which is very suitable for this problem. With a few modifications you can make it usable in python using f2py.
From the description:
BIVAR is a FORTRAN90 library which interpolates scattered bivariate data, by Hiroshi Akima.
BIVAR accepts a set of (X,Y) data points scattered in 2D, with associated Z data values, and is able to construct a smooth interpolation function Z(X,Y), which agrees with the given data, and can be evaluated at other points in the plane.

Categories

Resources