Vectors

NOTE: For technical reasons, all interactive-widgets plots in this documentation are created using Holoviz’s Panel. Often, they will ran just fine with ipywidgets too. However, if a specific example uses the param library, or widgets from the panel module, then users will have to modify the params dictionary in order to make it work with ipywidgets. Refer to Interactive module for more information.

spb.plot_functions.vectors.plot_vector(*args, **kwargs)[source]

Plot a 2D or 3D vector field. By default, the aspect ratio of the plot is set to aspect=”equal”.

Typical usage examples are in the followings:

  • Plotting a 2D vector field:

    plot_vector(vec, range_x, range_y, **kwargs)
    
  • Plotting multiple 2D vector fields with different ranges and custom labels:

    plot_vector(
       (vec1, range1_x, range1_y, label1 [opt]),
       (vec2, range2_x, range2_y, label2 [opt]),
       **kwargs)
    
  • Plotting a 3D vector field:

    plot_vector(vec, range_x, range_y, range_z, **kwargs)
    
  • Plotting multiple 3D vector fields with different ranges and custom labels:

    plot_vector(
       (vec1, range1_x, range1_y, range1_z, label1 [opt]),
       (vec2, range2_x, range2_y, range2_z, label2 [opt]),
       **kwargs)
    

Refer to vector_field_2d() for a full list of keyword arguments to customize the appearances of quivers, streamlines and contours for a 2D vector field.

Refer to vector_field_3d() for a full list of keyword arguments to customize the appearances of quivers and streamlines for a 3D vector field.

Refer to graphics() for a full list of keyword arguments to customize the appearances of the figure (title, axis labels, …).

Examples

>>> from sympy import symbols, sin, cos, Plane, Matrix, sqrt, latex
>>> from spb import plot_vector
>>> x, y, z = symbols('x, y, z')

(Source code)

Quivers plot of a 2D vector field with a contour plot in background representing the vector’s magnitude (a scalar field).

>>> v = [sin(x - y), cos(x + y)]
>>> plot_vector(v, (x, -3, 3), (y, -3, 3),
...     quiver_kw=dict(color="black", scale=30, headwidth=5),
...     contour_kw={"cmap": "Blues_r", "levels": 15},
...     grid=False, xlabel="x", ylabel="y")
Plot object containing:
[0]: contour: sqrt(sin(x - y)**2 + cos(x + y)**2) for x over (-3.0, 3.0) and y over (-3.0, 3.0)
[1]: 2D vector series: [sin(x - y), cos(x + y)] over (x, -3.0, 3.0), (y, -3.0, 3.0)

(Source code, png)

../../_images/vectors-2.png

Quivers plot of a 2D vector field with no background scalar field, a custom label and normalized quiver lengths:

>>> plot_vector(
...     v, (x, -3, 3), (y, -3, 3),
...     label="Magnitude of $%s$" % latex([-sin(y), cos(x)]),
...     scalar=False, normalize=True,
...     quiver_kw={
...         "scale": 35, "headwidth": 4, "cmap": "gray",
...         "clim": [0, 1.6]},
...     grid=False, xlabel="x", ylabel="y")
Plot object containing:
[0]: 2D vector series: [sin(x - y), cos(x + y)] over (x, -3.0, 3.0), (y, -3.0, 3.0)

(Source code, png)

../../_images/vectors-31.png

Streamlines plot of a 2D vector field with no background scalar field, and a custom label:

>>> plot_vector(v, (x, -3, 3), (y, -3, 3),
...     streamlines=True, scalar=None,
...     stream_kw={"density": 1.5},
...     label="Magnitude of %s" % str(v), xlabel="x", ylabel="y")
Plot object containing:
[0]: 2D vector series: [sin(x - y), cos(x + y)] over (x, -3.0, 3.0), (y, -3.0, 3.0)

(Source code, png)

../../_images/vectors-4.png

Plot multiple 2D vectors fields, setting a background scalar field to be the magnitude of the first vector. Also, apply custom rendering options to all data series.

>>> scalar_expr = sqrt((-sin(y))**2 + cos(x)**2)
>>> plot_vector([-sin(y), cos(x)], [2 * y, x], (x, -5, 5), (y, -3, 3),
...     n=20, legend=True, grid=False, xlabel="x", ylabel="y",
...     scalar=[scalar_expr, "$%s$" % latex(scalar_expr)], use_cm=False,
...     rendering_kw=[
...         {"cmap": "summer"}, # to the contour
...         {"color": "k"},     # to the first quiver
...         {"color": "r"}      # to the second quiver
... ])
Plot object containing:
[0]: contour: sqrt(sin(y)**2 + cos(x)**2) for x over (-5.0, 5.0) and y over (-3.0, 3.0)
[1]: 2D vector series: [-sin(y), cos(x)] over (x, -5.0, 5.0), (y, -3.0, 3.0)
[2]: 2D vector series: [2*y, x] over (x, -5.0, 5.0), (y, -3.0, 3.0)

(Source code, png)

../../_images/vectors-5.png

Plotting a the streamlines of a 2D vector field defined with numerical functions instead of symbolic expressions:

>>> import numpy as np
>>> f = lambda x, y: np.sin(2 * x + 2 * y)
>>> fx = lambda x, y: np.cos(f(x, y))
>>> fy = lambda x, y: np.sin(f(x, y))
>>> plot_vector([fx, fy], ("x", -1, 1), ("y", -1, 1),
...     streamlines=True, scalar=False, use_cm=False)  

(Source code, png)

../../_images/vectors-61.png

Interactive-widget 2D vector plot. Refer to the interactive sub-module documentation to learn more about the params dictionary. This plot illustrates:

  • customizing the appearance of quivers and countour.

  • the use of prange (parametric plotting range).

  • the use of the params dictionary to specify sliders in their basic form: (default, min, max).

  • the use of panel.widgets.slider.RangeSlider, which is a 2-values widget.

from sympy import *
from spb import *
import panel as pn
x, y, a, b, c, d, e = symbols("x, y, a, b, c, d, e")
v = [-sin(a * y), cos(b * x)]
plot_vector(
    v, prange(x, -3*c, 3*c), prange(y, d, e),
    params={
        a: (1, -2, 2), b: (1, -2, 2),
        c: (1, 0, 2),
        (d, e): pn.widgets.RangeSlider(
            value=(-3, 3), start=-9, end=9, step=0.1)
    },
    quiver_kw=dict(color="black", scale=30, headwidth=5),
    contour_kw={"cmap": "Blues_r", "levels": 15},
    grid=False, xlabel="x", ylabel="y",
    imodule="panel", servable=True
)

(Source code, small.png)

../../_images/vectors-7.small.png

3D vector field.

from sympy import *
from spb import *
var("x:z")
plot_vector([z, y, x], (x, -10, 10), (y, -10, 10), (z, -10, 10),
    backend=KB, n=8, xlabel="x", ylabel="y", zlabel="z",
    quiver_kw={"scale": 0.5, "line_width": 0.1, "head_size": 10})

(Source code, small.png)

../../_images/vectors-8.small.png

3D vector field with 3 orthogonal slice planes.

from sympy import *
from spb import *
var("x:z")
plot_vector([z, y, x], (x, -10, 10), (y, -10, 10), (z, -10, 10),
    backend=KB, n=8, use_cm=False, grid=False,
    xlabel="x", ylabel="y", zlabel="z",
    quiver_kw={"scale": 0.25, "line_width": 0.1, "head_size": 10},
    slice=[
        Plane((-10, 0, 0), (1, 0, 0)),
        Plane((0, 10, 0), (0, 2, 0)),
        Plane((0, 0, -10), (0, 0, 1))])

(Source code, small.png)

../../_images/vectors-9.small.png

3D vector streamlines starting at a 300 random points:

from sympy import *
from spb import *
import k3d
var("x:z")
plot_vector(Matrix([z, -x, y]), (x, -3, 3), (y, -3, 3), (z, -3, 3),
    backend=KB, n=40, streamlines=True,
    stream_kw=dict(
        starts=True,
        npoints=400,
        width=0.025,
        color_map=k3d.colormaps.matplotlib_color_maps.viridis
    ),
    xlabel="x", ylabel="y", zlabel="z")

(Source code, small.png)

../../_images/vectors-10.small.png

3D vector streamlines starting at the XY plane. Note that the number of discretization points of the plane controls the numbers of streamlines.

from sympy import *
from spb import *
import k3d
var("x:z")
u = -y - z
v = x + y / 5
w = S(1) / 5 + (x - S(5) / 2) * z
s = 10 # length of the cubic discretization volume
# create an XY plane with n discretization points along each direction
n = 8
p1 = plot_geometry(
    Plane((0, 0, 0), (0, 0, 1)), (x, -s, s), (y, -s, s), (z, -s, s),
    n1=n, n2=n, show=False)
# extract the coordinates of the starting points for the streamlines
xx, yy, zz = p1[0].get_data()
# streamlines plot
plot_vector(Matrix([u, v, w]), (x, -s, s), (y, -s, s), (z, -s, s),
    backend=KB, n=40, streamlines=True, grid=False,
    stream_kw=dict(
        starts=dict(x=xx, y=yy, z=zz),
        width=0.025,
        color_map=k3d.colormaps.matplotlib_color_maps.plasma
    ),
    title=r"Rössler \, attractor", xlabel="x", ylabel="y", zlabel="z")

(Source code, small.png)

../../_images/vectors-11.small1.png

Visually verify the normal vector to a circular cone surface. The following steps are executed:

  1. compute the normal vector to a circular cone surface. This will be the vector field to be plotted.

  2. plot the cone surface for visualization purposes (use high number of discretization points).

  3. plot the cone surface that will be used to slice the vector field (use a low number of discretization points). The data series associated to this plot will be used in the slice keyword argument in the next step.

  4. plot the sliced vector field.

  5. combine the plots of step 4 and 2 to get a nice visualization.

from sympy import tan, cos, sin, pi, symbols
from spb import plot3d_parametric_surface, plot_vector, KB
from sympy.vector import CoordSys3D, gradient

u, v = symbols("u, v")
N = CoordSys3D("N")
i, j, k = N.base_vectors()
xn, yn, zn = N.base_scalars()

t = 0.35    # half-cone angle in radians
expr = -xn**2 * tan(t)**2 + yn**2 + zn**2    # cone surface equation
g = gradient(expr)
n = g / g.magnitude()    # unit normal vector
n1, n2 = 10, 20 # number of discretization points for the vector field

# cone surface for visualization (high number of discretization points)
p1 = plot3d_parametric_surface(
    u / tan(t), u * cos(v), u * sin(v), (u, 0, 1), (v, 0 , 2*pi),
    {"opacity": 1}, backend=KB, show=False, wireframe=True,
    wf_n1=n1, wf_n2=n2, wf_rendering_kw={"width": 0.004})
# cone surface to discretize vector field (low numb of discret points)
p2 = plot3d_parametric_surface(
    u / tan(t), u * cos(v), u * sin(v), (u, 0, 1), (v, 0 , 2*pi),
    n1=n1, n2=n2, show=False)
# plot vector field on over the surface of the cone
p3 = plot_vector(
    n, (xn, -5, 5), (yn, -5, 5), (zn, -5, 5), slice=p2[0],
    backend=KB, use_cm=False, show=False,
    quiver_kw={"scale": 0.5, "pivot": "tail"})
(p1 + p3).show()

(Source code, small.png)

../../_images/vectors-12.small1.png