Skip to content

Commit d60c9fc

Browse files
committed
Remove integrator parameter for streamplot
* RK12 is 1.5--2x faster than RK4 and RK45 produces visually-similar results. * Remove implementations of RK4 and RK45.
1 parent 5632418 commit d60c9fc

File tree

2 files changed

+10
-137
lines changed

2 files changed

+10
-137
lines changed

lib/matplotlib/axes.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6305,8 +6305,7 @@ def quiver(self, *args, **kw):
63056305
quiver.__doc__ = mquiver.Quiver.quiver_doc
63066306

63076307
def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None,
6308-
cmap=None, arrowsize=1, arrowstyle='-|>', minlength=0.1,
6309-
integrator='RK4'):
6308+
cmap=None, arrowsize=1, arrowstyle='-|>', minlength=0.1):
63106309
if not self._hold: self.cla()
63116310
mstream.streamplot(self, x, y, u, v,
63126311
density=density,
@@ -6315,8 +6314,7 @@ def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None,
63156314
cmap=cmap,
63166315
arrowsize=arrowsize,
63176316
arrowstyle=arrowstyle,
6318-
minlength=minlength,
6319-
integrator=integrator)
6317+
minlength=minlength)
63206318
streamplot.__doc__ = mstream.streamplot.__doc__
63216319

63226320
@docstring.dedent_interpd

lib/matplotlib/streamplot.py

Lines changed: 8 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@
2121
THE SOFTWARE.
2222
2323
"""
24-
from operator import mul
25-
from itertools import imap
26-
2724
import numpy as np
2825
import matplotlib
2926
import matplotlib.patches as mpp
@@ -33,7 +30,7 @@
3330

3431

3532
def streamplot(axes, x, y, u, v, density=1, linewidth=1, color='k', cmap=None,
36-
arrowsize=1, arrowstyle='-|>', minlength=0.1, integrator='RK4'):
33+
arrowsize=1, arrowstyle='-|>', minlength=0.1):
3734
"""Draws streamlines of a vector flow.
3835
3936
Parameters
@@ -63,10 +60,6 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=1, color='k', cmap=None,
6360
Arrow style specification. See `matplotlib.patches.FancyArrowPatch`.
6461
minlength : float
6562
Minimum length of streamline in axes coordinates.
66-
integrator : {'RK4'|'RK45'}
67-
Integration scheme.
68-
RK4 = 4th-order Runge-Kutta
69-
RK45 = adaptive-step Runge-Kutta-Fehlberg
7063
"""
7164
grid = Grid(x, y)
7265
mask = StreamMask(density)
@@ -86,7 +79,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=1, color='k', cmap=None,
8679
assert u.shape == grid.shape
8780
assert v.shape == grid.shape
8881

89-
integrate = get_integrator(u, v, dmap, minlength, integrator)
82+
integrate = get_integrator(u, v, dmap, minlength)
9083

9184
trajectories = []
9285
for xm, ym in _gen_starting_points(mask.shape):
@@ -303,7 +296,7 @@ class InvalidIndexError(Exception):
303296
# Integrator definitions
304297
#========================
305298

306-
def get_integrator(u, v, dmap, minlength, integrator):
299+
def get_integrator(u, v, dmap, minlength):
307300

308301
# rescale velocity onto grid-coordinates for integrations.
309302
u, v = dmap.data2grid(u, v)
@@ -324,14 +317,7 @@ def backward_time(xi, yi):
324317
dxi, dyi = forward_time(xi, yi)
325318
return -dxi, -dyi
326319

327-
if integrator == 'RK4':
328-
_integrate = _rk4
329-
elif integrator == 'RK45':
330-
_integrate = _rk45
331-
elif integrator == 'RK12':
332-
_integrate = _rk12
333-
334-
def rk4_integrate(x0, y0):
320+
def integrate(x0, y0):
335321
"""Return x, y coordinates of trajectory based on starting point.
336322
337323
Integrate both forward and backward in time from starting point.
@@ -341,9 +327,9 @@ def rk4_integrate(x0, y0):
341327
"""
342328

343329
dmap.start_trajectory(x0, y0)
344-
sf, xf_traj, yf_traj = _integrate(x0, y0, dmap, forward_time)
330+
sf, xf_traj, yf_traj = _integrate_rk12(x0, y0, dmap, forward_time)
345331
dmap.reset_start_point(x0, y0)
346-
sb, xb_traj, yb_traj = _integrate(x0, y0, dmap, backward_time)
332+
sb, xb_traj, yb_traj = _integrate_rk12(x0, y0, dmap, backward_time)
347333
# combine forward and backward trajectories
348334
stotal = sf + sb
349335
x_traj = xb_traj[::-1] + xf_traj[1:]
@@ -355,10 +341,10 @@ def rk4_integrate(x0, y0):
355341
dmap.undo_trajectory()
356342
return None
357343

358-
return rk4_integrate
344+
return integrate
359345

360346

361-
def _rk12(x0, y0, dmap, f):
347+
def _integrate_rk12(x0, y0, dmap, f):
362348
"""2nd-order Runge-Kutta algorithm with adaptive step size.
363349
364350
This method is also referred to as the improved Euler's method, or Heun's
@@ -462,109 +448,6 @@ def _euler_step(xf_traj, yf_traj, dmap, f):
462448
return ds, xf_traj, yf_traj
463449

464450

465-
def _rk4(x0, y0, dmap, f):
466-
"""4th-order Runge-Kutta algorithm with fixed step size"""
467-
ds = min(1./dmap.mask.nx, 1./dmap.mask.ny, 0.01)
468-
stotal = 0
469-
xi = x0
470-
yi = y0
471-
xf_traj = []
472-
yf_traj = []
473-
474-
while dmap.grid.valid_index(xi, yi):
475-
# Time step. First save the point.
476-
xf_traj.append(xi)
477-
yf_traj.append(yi)
478-
# Next, advance one using RK4
479-
try:
480-
k1x, k1y = f(xi, yi)
481-
k2x, k2y = f(xi + .5*ds*k1x, yi + .5*ds*k1y)
482-
k3x, k3y = f(xi + .5*ds*k2x, yi + .5*ds*k2y)
483-
k4x, k4y = f(xi + ds*k3x, yi + ds*k3y)
484-
except IndexError:
485-
# Out of the domain on one of the intermediate steps
486-
break
487-
xi += ds*(k1x+2*k2x+2*k3x+k4x) / 6.
488-
yi += ds*(k1y+2*k2y+2*k3y+k4y) / 6.
489-
# Final position might be out of the domain
490-
491-
try:
492-
dmap.update_trajectory(xi, yi)
493-
except InvalidIndexError:
494-
break
495-
if (stotal + ds) > 2:
496-
break
497-
stotal += ds
498-
499-
return stotal, xf_traj, yf_traj
500-
501-
502-
def _rk45(x0, y0, dmap, f):
503-
"""5th-order Runge-Kutta algorithm with adaptive step size"""
504-
maxerror = 0.001
505-
maxds = min(1./dmap.mask.nx, 1./dmap.mask.ny, 0.03)
506-
ds = maxds
507-
stotal = 0
508-
xi = x0
509-
yi = y0
510-
xf_traj = []
511-
yf_traj = []
512-
513-
# RK45 coefficients (Runge-Kutta-Fehlberg method)
514-
a2 = 0.25
515-
a3 = (3./32, 9./32)
516-
a4 = (1932./2197, -7200./2197, 7296./2197)
517-
a5 = (439./216, -8, 3680./513, -845./4104)
518-
a6 = (-8./27, 2, -3544./2565, 1859./4104, -11./40)
519-
520-
b4 = (25./216, 1408./2565, 2197./4104, -1./5)
521-
b5 = (16./135, 6656./12825, 28561./56430, -9./50, 2./55)
522-
523-
while dmap.grid.valid_index(xi, yi):
524-
xf_traj.append(xi)
525-
yf_traj.append(yi)
526-
527-
try:
528-
k1x, k1y = f(xi, yi)
529-
k2x, k2y = f(xi + ds * a2 * k1x,
530-
yi + ds * a2 * k1y)
531-
k3x, k3y = f(xi + ds * dot(a3, (k1x, k2x)),
532-
yi + ds * dot(a3, (k1y, k2y)))
533-
k4x, k4y = f(xi + ds * dot(a4, (k1x, k2x, k3x)),
534-
yi + ds * dot(a4, (k1y, k2y, k3y)))
535-
k5x, k5y = f(xi + ds * dot(a5, (k1x, k2x, k3x, k4x)),
536-
yi + ds * dot(a5, (k1y, k2y, k3y, k4y)))
537-
k6x, k6y = f(xi + ds * dot(a6, (k1x, k2x, k3x, k4x, k5x)),
538-
yi + ds * dot(a6, (k1y, k2y, k3y, k4y, k5y)))
539-
except IndexError:
540-
# Out of the domain on one of the intermediate steps
541-
break
542-
543-
dx4 = ds * dot(b4, (k1x, k3x, k4x, k5x))
544-
dy4 = ds * dot(b4, (k1y, k3y, k4y, k5y))
545-
dx5 = ds * dot(b5, (k1x, k3x, k4x, k5x, k6x))
546-
dy5 = ds * dot(b5, (k1y, k3y, k4y, k5y, k6y))
547-
548-
nx, ny = dmap.grid.shape
549-
# Error is normalized to the axes coordinates
550-
error = np.sqrt(((dx5-dx4)/nx)**2 + ((dy5-dy4)/ny)**2)
551-
552-
# Only save step if within error tolerance
553-
if error < maxerror:
554-
xi += dx5
555-
yi += dy5
556-
try:
557-
dmap.update_trajectory(xi, yi)
558-
except InvalidIndexError:
559-
break
560-
if (stotal + ds) > 2:
561-
break
562-
stotal += ds
563-
564-
# recalculate stepsize based on Runge-Kutta-Fehlberg method
565-
ds = min(maxds, 0.85 * ds * (maxerror/error)**0.2)
566-
return stotal, xf_traj, yf_traj
567-
568451
# Utility functions
569452
#========================
570453

@@ -598,14 +481,6 @@ def interpgrid(a, xi, yi):
598481
return a0 * (1 - yt) + a1 * yt
599482

600483

601-
def dot(seq1, seq2):
602-
"""Dot product of two sequences.
603-
604-
For short sequences, this is faster than transforming to numpy arrays.
605-
"""
606-
return sum(imap(mul, seq1, seq2))
607-
608-
609484
def _gen_starting_points(shape):
610485
"""Yield starting points for streamlines.
611486

0 commit comments

Comments
 (0)