Matplotlib quiver plot vectors not perpendicular to contours

Viewed 152

I want to create a contour plot of the function f(x, y) = x + 0.1xy and plot its gradient field on top. The problem: when variables of the function have different ranges, the gradient vectors are not perpendicular to the contours.

The function:

def f(x, y):
    return x + 0.1 * x * y

Gradients:

def grad_f(x, y, norm=True):    
    dfdx = 1 + 0.1 * y
    dfdy = 0.1 * x
    if norm:
        norm = np.linalg.norm((dfdx, dfdy), axis=0)
        dfdx /= norm
        dfdy /= norm
    return dfdx, dfdy

All works well here:

x = np.linspace(0, 10, 10)
y = np.linspace(0, 10, 10)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
dfdx, dfdy = grad_f(X, Y)

ax.contour(X, Y, Z, levels=np.arange(1, 20, 2))
ax.quiver(X, Y, dfdx, dfdy, Z)

Good quiver plot

But suppose I want to plot variables with different ranges. Then gradient vectors no longer appear perpendicular to the contours.

x = np.linspace(0, 5, 10) # Range of x is [0, 5]
y = np.linspace(0, 10, 10) 
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
dfdx, dfdy = grad_f(X, Y)

ax.contour(X, Y, Z, levels=np.arange(1, 20, 2))
ax.quiver(X, Y, dfdx, dfdy, Z)

Bad quiver plot

I've tried:

  • Using set_xlim and set_ylim to restrict the plotting area (instead of having different ranges in the call to np.linspace)
  • Setting angles="xy" and units="xy" in the call to quiver, per this
  • Experimenting with other parameters, such as scale_units

Neither of these options solved the problem. What seems to have helped is this:

scale = max(y) / max(x)
ax.quiver(X, Y, dfdx, dfdy*scale, Z)

However, I'm not sure if this is indeed the right solution. What is the correct way to plot a quiver plot when the variables have different ranges?

0 Answers
Related