2121THE SOFTWARE.
2222
2323"""
24- from operator import mul
25- from itertools import imap
26-
2724import numpy as np
2825import matplotlib
2926import matplotlib .patches as mpp
3330
3431
3532def 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-
609484def _gen_starting_points (shape ):
610485 """Yield starting points for streamlines.
611486
0 commit comments