I am trying to find areas or points where there is convergence in a vector field.
I have used the code below to generate the following plot:
import matplotlib.pyplot as plt
import numpy as np
def generate_fake_data():
return -(np.sin(X) * np.cos(Y) + np.cos(X)), -(-np.cos(X) * np.sin(Y) + np.sin(Y))
x = np.arange(0, 2 * np.pi + 2 * np.pi / 20, 2 * np.pi / 20)
y = np.arange(0, 2 * np.pi + 2 * np.pi / 20, 2 * np.pi / 20)
X, Y = np.meshgrid(x, y)
u, v = generate_fake_data()
fig, ax = plt.subplots(figsize=(7, 7))
# quiveropts = dict(headlength=0, headaxislength=0, pivot='middle', units='xy')
# ax.quiver(X, Y, u, v, **quiveropts)
ax.quiver(X, Y, u, v)
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
ax.axis([0, 2 * np.pi, 0, 2 * np.pi])
ax.set_aspect('equal')
ax.axis("off")
plt.gca().set_axis_off()
plt.subplots_adjust(top=1, bottom=0, right=1, left=0,
hspace=0, wspace=0)
plt.margins(0, 0)
plt.gca().xaxis.set_major_locator(plt.NullLocator())
plt.gca().yaxis.set_major_locator(plt.NullLocator())
plt.savefig("mock_data.png", bbox_inches='tight', pad_inches=0)
Ideally, what I am after is to find where there is convergence in this vector field in the top right and bottom right of the image.
I hope that the curl values could be used to achieve this, but any method is good to use.
Also, this is just a proof of concept, generate_fake_data will be replaced with a function reading in data from elsewhere that's changeable.
For converging points the divergence of the vector field is < 0.
from functools import reduce
conv = reduce(np.add,np.gradient(u)) + reduce(np.add,np.gradient(v))
(see Compute divergence of vector field using python)
We only need negativ divergence:
conv[conv>=0] = np.nan
plt.imshow(conv): the darker the more converging is the field:
Finding the absolute minimum (in the top right) is easy:
absmin = np.unravel_index(np.nanargmin(conv), conv.shape)
print(absmin, conv[absmin])
#(0, 15) -0.6669774724547413
Finding relative minima is harder, it should be possible with argrelmin, but frankly speaking I couldn't get it work correctly to return the second local minimum at (19,15).
Using this answer I get
lm = detect_local_minima(conv)
list(zip(lm))
[(0, 15), (1, 0), (19, 15), (20, 0)]
which also includes both minima in the left corners (which is mathematically correct, but not desired in our case, so maybe we could just exclude corners).
Related
I'm interested in plotting a real-valued function f(x,y,z)=a, where (x,y,z) is a 3D point on the sphere and a is a real number. I calculate the Cartesian coordinates of the points of the sphere as follows, but I have no clue on how to visualize the value of f on each of those points.
import plotly.graph_objects as go
import numpy as np
fig = go.Figure(layout=go.Layout(title=go.layout.Title(text=title), hovermode=False))
# Create mesh grid for spherical coordinates
phi, theta = np.mgrid[0.0:np.pi:100j, 0.0:2.0 * np.pi:100j]
# Get Cartesian mesh grid
x = np.sin(phi) * np.cos(theta)
y = np.sin(phi) * np.sin(theta)
z = np.cos(phi)
# Plot sphere surface
self.fig.add_surface(x=x, y=y, z=z, opacity=0.35)
fig.show()
I would imagine/expect/like a visualization like this
Additionally, I also have the gradient of f calculated in closed-form (i.e., for each (x,y,z) I calculate the 3D-dimensional gradient of f). Is there a way of plotting this vector field, similarly to what is shown in the figure above?
Here's an answer that's far from perfect, but hopefully that's enough for you to build on.
For the sphere itself, I don't know of any "shortcut" to do something like that in plotly, so my approach is simply to manually create a sphere mesh. Generating the vertices is simple, for example like you did - the slightly more tricky part is figuring out the vertex indices for the triangles (which depends on the vertex generation scheme). There are various algorithms to do that smoothly (i.e. generating a sphere with no "tip"), I hacked something crude just for the demonstration. Then we can use the Mesh3d object to display the sphere along with the intensities and your choice of colormap:
N = 100 # Sphere resolution (both rings and segments, can be separated to different constants)
theta, z = np.meshgrid(np.linspace(-np.pi, np.pi, N), np.linspace(-1, 1, N))
r = np.sqrt(1 - z ** 2)
x = r * np.cos(theta)
y = r * np.sin(theta)
x = x.ravel()
y = y.ravel()
z = z.ravel()
# Triangle indices
indices = np.arange(N * (N - 1) - 1)
i1 = np.concatenate([indices, (indices // N + 1) * N + (indices + 1) % N])
i2 = np.concatenate([indices + N, indices // N * N + (indices + 1) % N])
i3 = np.concatenate([(indices // N + 1) * N + (indices + 1) % N, indices])
# Point intensity function
def f(x, y, z):
return (np.cos(x * 2) + np.sin(y ** 2) + np.sin(z) + 3) / 6
fig = go.Figure(data=[
go.Mesh3d(
x=x,
y=y,
z=z,
colorbar_title='f(x, y, z)',
colorscale=[[0, 'gold'],
[0.5, 'mediumturquoise'],
[1, 'magenta']],
intensity = f(x, y, z),
i = i1,
j = i2,
k = i3,
name='y',
showscale=True
)
])
fig.show()
This yields the following interactive plot:
To add the vector field you can use the Cone plot; this requires some tinkering because when I simply draw the cones at the same x, y, z position as the sphere, some of the cones are partially or fully occluded by the sphere. So I generate another sphere, with a slightly larger radius, and place the cones there. I also played with some lighting parameters to make it black like in your example. The full code looks like this:
N = 100 # Sphere resolution (both rings and segments, can be separated to different constants)
theta, z = np.meshgrid(np.linspace(-np.pi, np.pi, N), np.linspace(-1, 1, N))
r = np.sqrt(1 - z ** 2)
x = r * np.cos(theta)
y = r * np.sin(theta)
x = x.ravel()
y = y.ravel()
z = z.ravel()
# Triangle indices
indices = np.arange(N * (N - 1) - 1)
i1 = np.concatenate([indices, (indices // N + 1) * N + (indices + 1) % N])
i2 = np.concatenate([indices + N, indices // N * N + (indices + 1) % N])
i3 = np.concatenate([(indices // N + 1) * N + (indices + 1) % N, indices])
# Point intensity function
def f(x, y, z):
return (np.cos(x * 2) + np.sin(y ** 2) + np.sin(z) + 3) / 6
# Vector field function
def grad_f(x, y, z):
return np.stack([np.cos(3 * y + 5 * x),
np.sin(z * y),
np.cos(4 * x - 3 * y + z * 7)], axis=1)
# Second sphere for placing cones
N2 = 50 # Smaller resolution (again rings and segments combined)
R2 = 1.05 # Slightly larger radius
theta2, z2 = np.meshgrid(np.linspace(-np.pi, np.pi, N2), np.linspace(-R2, R2, N2))
r2 = np.sqrt(R2 ** 2 - z2 ** 2)
x2 = r2 * np.cos(theta2)
y2 = r2 * np.sin(theta2)
x2 = x2.ravel()
y2 = y2.ravel()
z2 = z2.ravel()
uvw = grad_f(x2, y2, z2)
fig = go.Figure(data=[
go.Mesh3d(
x=x,
y=y,
z=z,
colorbar_title='f(x, y, z)',
colorscale=[[0, 'gold'],
[0.5, 'mediumturquoise'],
[1, 'magenta']],
intensity = f(x, y, z),
i = i1,
j = i2,
k = i3,
name='y',
showscale=True
),
go.Cone(
x=x2, y=y2, z=z2, u=uvw[:, 0], v=uvw[:, 1], w=uvw[:, 2], sizemode='absolute', sizeref=2, anchor='tail',
lighting_ambient=0, lighting_diffuse=0, opacity=.2
)
])
fig.show()
And yields this plot:
Hope this helps. There are a lot of tweaks to the display, and certainly better ways to construct a sphere mesh (e.g. see this article), so there should be a lot of freedom there (albeit at the cost of some work).
Good luck!
I need some help with visualization in Matplotlib. I'm doing some Computational Fluid Dynamics and I have this kinda mesh, in which you can see the circular shape of floor. I've run some computations and from the data I gathered, I got this image of isolines
of Mach number. As you can see, the curved floor isn't present. How can I plot the isolines in the specified curved shape of the mesh?
This is my code:
mach = np.ndarray((nx, ny))
x = np.empty([nx])
y = np.empty([ny])
for index, cell in np.ndenumerate(mach):
p = (kappa - 1) * (w[index][3] - 0.5 *
(w[index][1] * w[index][1] + w[index][2] * w[index][2])
/ w[index][0])
v = np.sqrt((w[index][1] / w[index][0]) * (w[index][1] / w[index][0]) + (w[index][2] / w[index][0]) * (
w[index][2] / w[index][0]))
a = np.sqrt(kappa * p / w[index][0])
mach[index] = v / a
x[index[0]] = Mesh[index][0]
y[index[1]] = Mesh[index][1]
plt.figure(1)
X, Y = np.meshgrid(x, y, indexing='ij')
matplotlib.pyplot.contour(X, Y, mach, 50, cmap='turbo')
plt.show()
Thank you for your answers!
I am trying to plot some phase planes with limit circles. I have the differential equations in polar form:
drdt = r(r^2 -1) dθdt = 1
is there a way to plot them from this using matplotlib? I was able to plot one that I have the original system form by using the original system like this:
xvalues, yvalues = np.meshgrid(np.arange(-2, 2, 0.1), np.arange(-2, 2, 0.1))
xdot = xvalues + yvalues - xvalues * (xvalues ** 2 + yvalues ** 2)
ydot = -xvalues + yvalues - yvalues * (xvalues ** 2 + yvalues ** 2)
plt.streamplot(xvalues, yvalues, xdot, ydot)
plt.grid();
plt.show()
this system is
dxdt = x + y - x(x^2 + y2)
dydt = -x + y - y(x^2 + y2)
and its polar form is:
drdt = r(1-r^2) dθdt = 1
But it would be tedious to covert them all to the original system, if its even possible. Appreciate any help I can get.
Update
So I came up with a method that comes close enough by solving the differential with scipy for a range of initial radii, and then plotting on X, Y with the conversion formula
times = np.linspace(0, 10)
r0s = np.arange(0, 3, 0.2)
for r0 in r0s:
z0 = [r0, r0]
solution = odeint(rmodel, z0, times)
x = solution[:,0] * np.cos(solution[:,1])
y = solution[:,0] * np.sin(solution[:,1])
plt.plot(x, y)
This works well enough for now but I would really like a better solution.
So I came up with a method that comes close enough by solving the differential with scipy for a range of initial radii, and then plotting on X, Y with the conversion formula
times = np.linspace(0, 10)
r0s = np.arange(0, 3, 0.2)
for r0 in r0s:
z0 = [r0, r0]
solution = odeint(rmodel, z0, times)
x = solution[:,0] * np.cos(solution[:,1])
y = solution[:,0] * np.sin(solution[:,1])
plt.plot(x, y)
This works well enough for my purposes.
I have an numpy array that represents my voxelgrid.. Now i want to add values to the surface of a sphere for a given radius. What is the fastest way?
My solution:
def spheric Surface (x, y, z, r, value):
while phi <= (2*math.pi):
eta = math.pi * 2 / 3
while eta <= math.pi:
xx = x + r * math.sin(eta) * math.cos(phi)
yy = y + r * math.sin(eta) * math.sin(phi)
zz = z + r * math.cos(eta)
xx = int(xx*resoultion+0.5)
yy = int(yy*resolution+0.5)
zz = int(zz*resolution+0.5)
voxelGrid[xx][yy][zz] += value
eta += 1/10 * math.pi
phi += 1/10 * math.pi
This is my first Idea: It ist not very fast and not very accurate because with bigger r, i need more angle to calculate.., not just adding 1/10pi for example but 1/5pi, but this makes the code even slower...
Resolution is the resolution of my voxelgrid.. so with Resolution 3, x=2mm would become xx= 6 in the array..
And yes i dont want the whole surface of the sphere, just from 2/3pi to pi...
Is there any better and faster way?
I tried the way with the mask like this, but it is even slower:
def sphericSurface(x, y, z, r, value):
tol = 0.6
grenz = math.pi * 2 / 3
mask = (np.logical_and(np.logical_and((sx[:, None, None] - x) ** 2 + (sy[None, :, None] - y) ** 2 + (sz[None, None, :] - z) ** 2 <= (r + tol)**2,
(sx[:, None, None] - x) ** 2 + (sy[None, :, None] - y) ** 2 + (sz[None, None, :] - z) ** 2 >= (r - tol)**2),
(sz[None, None, :] - z) <= (r*math.cos(grenz))))
x, y, z = np.where(mask==True)
z *= 2
voxelGrid[x,y,z] += value
You can select all of the elements that require modification by generating a mask. I'm not sure how compatible this is which what you already have, but this is the way. It'll basically blow the doors off of the while loop solution speed-wise.
import numpy as np
x = np.arange(0.0,5.0,0.1)
y = np.arange(0.0,5.0,0.1)
z = np.arange(0.0,5.0,0.1)
points = np.array(np.meshgrid(x,y,z)).T
def make_mask(points,a,b,c,r,tol=1e-2):
"""generates a boolean mask of positions within tol distance of the surface of the sphere
(x-a)**2 + (y-b)**2 + (z-c)**2 = r**2"""
mask = (points[...,0]-a)**2+(points[...,1]-b)**2+(points[...,2]-c)**2 < (r+tol)**2
return mask
mask = make_mask(points,2.5,2.5,2.5,1.0,tol=0.2)
# this will tell you all of the points in voxelgrid which need modification
voxelgrid[mask] #will return them all
If you want to add a value to every point near the surface of the sphere you can do
voxelgrid[mask]+=value
provided that the voxelgrid and points coordinates coincide in the sense that voxelgrid[i,j,k] is the container associated with the point points[i,j,k].. you will have to use your resolution parameter to make the x,y,z so that this is true.
Here's a lame plot showing that it works for me:
The code for this plot is
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(*points[mask].T)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
plt.savefig('works.png')
You can maybe calculate the mask more cleanly with something like:
x0 = np.array([a,b,c])
mask = np.sum((points-x0)**2,axis=-1)<(r+tol)**2
but it's a little harder to read. It may be faster ? I am not sure on this. (can anyone weigh in? )
I'm trying to recreate the diagram (or just a contour plot would be okay) for the Dirichlet distribution that's on Wikipedia using matplotlib and numpy. I am having trouble easily generating a triangular contourf. The first problem is that meshgrid doesn't return a triangle of points. Even if I get a triangle of points, will contourf handle the non-rectangular input?
Here's what I have so far:
#!/usr/bin/env python
from __future__ import division
import matplotlib
matplotlib.use("TkAgg")
matplotlib.rc('text', usetex=True)
matplotlib.rcParams['text.latex.preamble']=r"""\usepackage{amsmath}
"""
import math
import scipy.special
root_three_over_two = np.sqrt(3) / 2
def new_figure():
# 1.45
plt.figure(figsize = [2.6, 2.6 * root_three_over_two], dpi = 1200)
plt.axes([0.05, 0.10, 0.90, 0.90], frameon = False)
xsize = 1.0
ysize = root_three_over_two * xsize
plt.axis([0, xsize, 0, ysize])
resolution = 0.05
R = inclusive_arange(0.0, 1.0, resolution)
x, y = np.meshgrid(inclusive_arange(0.0, 1.0, resolution),
inclusive_arange(0.0, 1.0, resolution))
# UNFORTUNATELY x, and y include a lot of points where x+y>1
x = []
y = []
for yy in R:
x.append(list(inclusive_arange(0.0, 1.0 - yy, resolution)))
y.append([yy for xx in R])
print x
print y
z = 1 - x - y
# We can use these to convert to and from the equilateral triangle.
M = [[1, 0.5], [0, root_three_over_two]]
Mi = np.linalg.inv(M)
def dirichlet(x, y, z, a, b, c):
if z < 0:
return 0
return x ** (a - 1) * y ** (b - 1) * z ** (c - 1) \
* math.gamma(a + b + c) \
/ (math.gamma(a) * math.gamma(b) * math.gamma(c))
dirichlet = np.frompyfunc(dirichlet, 6, 1)
for (dirichlet_parm, filename) in [((5.0, 1.5, 2.5), "dir_small.pdf")]:
new_figure()
height = dirichlet(x, y, z, *dirichlet_parm)
M = np.max(height)
cs = plt.contourf(x, y, height, 50)
S = sum(dirichlet_parm)
plt.savefig(filename)
You do not need a rectilinear meshgrid to create a contour plot. You can define the perimeter and perform a Delaunay triangulation. The coordinates, of course will still be rectilinear and (it appears) you'll need to do a transformation. This example should be enough to create a 2D contour. I have produced some 3D surface plots with a non-rectangle perimeter and you get some edge artifacts that can be unsightly. A very fine grid might ameliorate some of these.