{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Streamplot\n\nA stream plot, or streamline plot, is used to display 2D vector fields. This\nexample shows a few features of the `~.axes.Axes.streamplot` function:\n\n* Varying the color along a streamline.\n* Varying the density of streamlines.\n* Varying the line width along a streamline.\n* Controlling the starting points of streamlines.\n* Streamlines skipping masked regions and NaN values.\n* Unbroken streamlines even when exceeding the limit of lines within a single\n grid cell.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import time\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nw = 3\nY, X = np.mgrid[-w:w:100j, -w:w:100j]\nU = -1 - X**2 + Y\nV = 1 + X - Y**2\nspeed = np.sqrt(U**2 + V**2)\n\nfig, axs = plt.subplots(4, 2, figsize=(7, 12), height_ratios=[1, 1, 1, 2])\naxs = axs.flat\n\n# Varying density along a streamline\naxs[0].streamplot(X, Y, U, V, density=[0.5, 1])\naxs[0].set_title('Varying Density')\n\n# Varying color along a streamline\nstrm = axs[1].streamplot(X, Y, U, V, color=U, linewidth=2, cmap='autumn')\nfig.colorbar(strm.lines)\naxs[1].set_title('Varying Color')\n\n# Varying line width along a streamline\nlw = 5*speed / speed.max()\naxs[2].streamplot(X, Y, U, V, density=0.6, color='k', linewidth=lw, num_arrows=5)\naxs[2].set_title('Varying Line Width')\n\n# Controlling the starting points of the streamlines\nseed_points = np.array([[-2, -1, 0, 1, 2, -1], [-2, -1, 0, 1, 2, 2]])\n\nstrm = axs[3].streamplot(X, Y, U, V, color=U, linewidth=2,\n cmap='autumn', start_points=seed_points.T)\nfig.colorbar(strm.lines)\naxs[3].set_title('Controlling Starting Points')\n\n# Displaying the starting points with blue symbols.\naxs[3].plot(seed_points[0], seed_points[1], 'bo')\naxs[3].set(xlim=(-w, w), ylim=(-w, w))\n\n# Adding more than one arrow to each streamline\naxs[4].streamplot(X, Y, U, V, num_arrows=3)\naxs[4].set_title('Multiple arrows')\n\naxs[5].axis(\"off\")\n\n# Create a mask\nmask = np.zeros(U.shape, dtype=bool)\nmask[40:60, 40:60] = True\nU[:20, :20] = np.nan\nU = np.ma.array(U, mask=mask)\n\naxs[6].streamplot(X, Y, U, V, color='r')\naxs[6].set_title('Streamplot with Masking')\n\naxs[6].imshow(~mask, extent=(-w, w, -w, w), alpha=0.5, cmap='gray',\n aspect='auto')\naxs[6].set_aspect('equal')\n\naxs[7].streamplot(X, Y, U, V, broken_streamlines=False)\naxs[7].set_title('Streamplot with unbroken streamlines')\n\nplt.tight_layout()\n# plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Streamline computation\n\nThe streamlines are computed by integrating along the provided vector field\nfrom the seed points, which are either automatically generated or manually\nspecified. The accuracy and smoothness of the streamlines can be adjusted using\nthe ``integration_max_step_scale`` and ``integration_max_error_scale`` optional\nparameters. See the `~.axes.Axes.streamplot` function documentation for more\ndetails.\n\nThis example shows how adjusting the maximum allowed step size and error for\nthe integrator changes the appearance of the streamline. The differences can\nbe subtle, but can be observed particularly where the streamlines have\nhigh curvature (as shown in the zoomed in region).\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Linear potential flow over a lifting cylinder\nn = 50\nx, y = np.meshgrid(np.linspace(-2, 2, n), np.linspace(-3, 3, n))\nth = np.arctan2(y, x)\nr = np.sqrt(x**2 + y**2)\nvr = -np.cos(th) / r**2\nvt = -np.sin(th) / r**2 - 1 / r\nvx = vr * np.cos(th) - vt * np.sin(th) + 1.0\nvy = vr * np.sin(th) + vt * np.cos(th)\n\n# Seed points\nn_seed = 50\nseed_pts = np.column_stack((np.full(n_seed, -1.75), np.linspace(-2, 2, n_seed)))\n\n_, axs = plt.subplots(3, 1, figsize=(6, 14))\nth_circ = np.linspace(0, 2 * np.pi, 100)\nfor ax, max_val in zip(axs, [0.05, 1, 5]):\n ax_ins = ax.inset_axes([0.0, 0.7, 0.3, 0.35])\n for ax_curr, is_inset in zip([ax, ax_ins], [False, True]):\n t_start = time.time()\n ax_curr.streamplot(\n x,\n y,\n vx,\n vy,\n start_points=seed_pts,\n broken_streamlines=False,\n arrowsize=1e-10,\n linewidth=2 if is_inset else 0.6,\n color=\"k\",\n integration_max_step_scale=max_val,\n integration_max_error_scale=max_val,\n )\n if is_inset:\n t_total = time.time() - t_start\n\n # Draw the cylinder\n ax_curr.fill(\n np.cos(th_circ),\n np.sin(th_circ),\n color=\"w\",\n ec=\"k\",\n lw=6 if is_inset else 2,\n )\n\n # Set axis properties\n ax_curr.set_aspect(\"equal\")\n\n # Label properties of each circle\n text = f\"integration_max_step_scale: {max_val}\\n\" \\\n f\"integration_max_error_scale: {max_val}\\n\" \\\n f\"streamplot time: {t_total:.2f} sec\"\n if max_val == 1:\n text += \"\\n(default)\"\n ax.text(0.0, 0.0, text, ha=\"center\", va=\"center\")\n\n # Set axis limits and show zoomed region\n ax_ins.set_xlim(-1.2, -0.7)\n ax_ins.set_ylim(-0.8, -0.4)\n ax_ins.set_yticks(())\n ax_ins.set_xticks(())\n\n ax.set_ylim(-1.5, 1.5)\n ax.axis(\"off\")\n ax.indicate_inset_zoom(ax_ins, ec=\"k\")\n\nplt.tight_layout()\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ".. admonition:: References\n\n The use of the following functions, methods, classes and modules is shown\n in this example:\n\n - `matplotlib.axes.Axes.streamplot` / `matplotlib.pyplot.streamplot`\n - `matplotlib.gridspec.GridSpec`\n\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.15" } }, "nbformat": 4, "nbformat_minor": 0 }