I am trying to draw a potential field for a given object using the following formula:
U=-α_goal*e^(-((x-x_goal )^2/a_goal +(y-y_goal^2)/b_goal ) )
using the following code
# Set limits and number of points in grid
xmax = 10.0
xmin = -xmax
NX = 20
ymax = 10.0
ymin = -ymax
NY = 20
# Make grid and calculate vector components
x = linspace(xmin, xmax, NX)
y = linspace(ymin, ymax, NY)
X, Y = meshgrid(x, y)
x_obstacle = 0
y_obstacle = 0
alpha_obstacle = 1
a_obstacle = 1
b_obstacle = 1
P = -alpha_obstacle * exp(-(X - x_obstacle)**2 / a_obstacle + (Y - y_obstacle)**2 / b_obstacle)
Ey,Ex = gradient(P)
print Ey
print Ex
QP = quiver(X, Y, Ex, Ey)
show()
This code calculates a potential field. How can I plot this potential field nicely? Also, given a potential field, what is the best way to convert it to a vector field? (vector field is the minus gradient of the potential field. )
I would appreciate any help.
I have tried using np.gradient() but the result is not what I have expected:
What I do expect, is something along these lines:
EDIT: After changing the two lines in the code:
y, x = np.mgrid[500:-100:200j, 1000:-100:200j]
p = -1 * np.exp(-((x - 893.6)**2 / 1000 + (y - 417.35)**2 / 1000))
I have an incorrect plot: it seems to be inverted left-right (arrows seem to be in correct spot but not the field):
EDIT:
Fixed by changing to y, x = np.mgrid[500:-100:200j, -100:1000:200j]
Any idea why?
First off, let's evaluate it on a regular grid, similar to your example code. (On a side note, you have an error in the code to evaluate your equation. It's missing a negative inside the exp
.):
import numpy as np
import matplotlib.pyplot as plt
# Set limits and number of points in grid
y, x = np.mgrid[10:-10:100j, 10:-10:100j]
x_obstacle, y_obstacle = 0.0, 0.0
alpha_obstacle, a_obstacle, b_obstacle = 1.0, 1e3, 2e3
p = -alpha_obstacle * np.exp(-((x - x_obstacle)**2 / a_obstacle
+ (y - y_obstacle)**2 / b_obstacle))
Next, we'll need to calculate the gradient (this is a simple finite-difference, as opposed to analytically calculating the derivative of the function above):
# For the absolute values of "dx" and "dy" to mean anything, we'll need to
# specify the "cellsize" of our grid. For purely visual purposes, though,
# we could get away with just "dy, dx = np.gradient(p)".
dy, dx = np.gradient(p, np.diff(y[:2, 0]), np.diff(x[0, :2]))
Now we can make a "quiver" plot, however, the results probably won't be quite what you'd expect, as an arrow is being displayed at every point on the grid:
fig, ax = plt.subplots()
ax.quiver(x, y, dx, dy, p)
ax.set(aspect=1, title='Quiver Plot')
plt.show()
Let's make the arrows bigger. The easiest way to do this is to plot every n-th arrow and let matplotlib handle the autoscaling. We'll use every 3rd point here. If you want fewer, larger arrows, change the 3 to a larger integer number.
# Every 3rd point in each direction.
skip = (slice(None, None, 3), slice(None, None, 3))
fig, ax = plt.subplots()
ax.quiver(x[skip], y[skip], dx[skip], dy[skip], p[skip])
ax.set(aspect=1, title='Quiver Plot')
plt.show()
Better, but those arrows are still pretty hard to see. A better way to visualize this might be with an image plot with black gradient arrows overlayed:
skip = (slice(None, None, 3), slice(None, None, 3))
fig, ax = plt.subplots()
im = ax.imshow(p, extent=[x.min(), x.max(), y.min(), y.max()])
ax.quiver(x[skip], y[skip], dx[skip], dy[skip])
fig.colorbar(im)
ax.set(aspect=1, title='Quiver Plot')
plt.show()
Ideally, we'd want to use a different colormap or change the arrow colors. I'll leave that part to you. You might also consider a contour plot (ax.contour(x, y, p)
) or a streamplot (ax.streamplot(x, y, dx, dy
). Just to show a quick example of those:
fig, ax = plt.subplots()
ax.streamplot(x, y, dx, dy, color=p, density=0.5, cmap='gist_earth')
cont = ax.contour(x, y, p, cmap='gist_earth')
ax.clabel(cont)
ax.set(aspect=1, title='Streamplot with contours')
plt.show()
...And just for the sake of getting really fancy:
from matplotlib.patheffects import withStroke
fig, ax = plt.subplots()
ax.streamplot(x, y, dx, dy, linewidth=500*np.hypot(dx, dy),
color=p, density=1.2, cmap='gist_earth')
cont = ax.contour(x, y, p, cmap='gist_earth', vmin=p.min(), vmax=p.max())
labels = ax.clabel(cont)
plt.setp(labels, path_effects=[withStroke(linewidth=8, foreground='w')])
ax.set(aspect=1, title='Streamplot with contours')
plt.show()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With