21
21
THE SOFTWARE.
22
22
23
23
"""
24
- from operator import mul
25
- from itertools import imap
26
-
27
24
import numpy as np
28
25
import matplotlib
29
26
import matplotlib .patches as mpp
33
30
34
31
35
32
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 ):
37
34
"""Draws streamlines of a vector flow.
38
35
39
36
Parameters
@@ -63,10 +60,6 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=1, color='k', cmap=None,
63
60
Arrow style specification. See `matplotlib.patches.FancyArrowPatch`.
64
61
minlength : float
65
62
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
70
63
"""
71
64
grid = Grid (x , y )
72
65
mask = StreamMask (density )
@@ -86,7 +79,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=1, color='k', cmap=None,
86
79
assert u .shape == grid .shape
87
80
assert v .shape == grid .shape
88
81
89
- integrate = get_integrator (u , v , dmap , minlength , integrator )
82
+ integrate = get_integrator (u , v , dmap , minlength )
90
83
91
84
trajectories = []
92
85
for xm , ym in _gen_starting_points (mask .shape ):
@@ -303,7 +296,7 @@ class InvalidIndexError(Exception):
303
296
# Integrator definitions
304
297
#========================
305
298
306
- def get_integrator (u , v , dmap , minlength , integrator ):
299
+ def get_integrator (u , v , dmap , minlength ):
307
300
308
301
# rescale velocity onto grid-coordinates for integrations.
309
302
u , v = dmap .data2grid (u , v )
@@ -324,14 +317,7 @@ def backward_time(xi, yi):
324
317
dxi , dyi = forward_time (xi , yi )
325
318
return - dxi , - dyi
326
319
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 ):
335
321
"""Return x, y coordinates of trajectory based on starting point.
336
322
337
323
Integrate both forward and backward in time from starting point.
@@ -341,9 +327,9 @@ def rk4_integrate(x0, y0):
341
327
"""
342
328
343
329
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 )
345
331
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 )
347
333
# combine forward and backward trajectories
348
334
stotal = sf + sb
349
335
x_traj = xb_traj [::- 1 ] + xf_traj [1 :]
@@ -355,10 +341,10 @@ def rk4_integrate(x0, y0):
355
341
dmap .undo_trajectory ()
356
342
return None
357
343
358
- return rk4_integrate
344
+ return integrate
359
345
360
346
361
- def _rk12 (x0 , y0 , dmap , f ):
347
+ def _integrate_rk12 (x0 , y0 , dmap , f ):
362
348
"""2nd-order Runge-Kutta algorithm with adaptive step size.
363
349
364
350
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):
462
448
return ds , xf_traj , yf_traj
463
449
464
450
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
-
568
451
# Utility functions
569
452
#========================
570
453
@@ -598,14 +481,6 @@ def interpgrid(a, xi, yi):
598
481
return a0 * (1 - yt ) + a1 * yt
599
482
600
483
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
-
609
484
def _gen_starting_points (shape ):
610
485
"""Yield starting points for streamlines.
611
486
0 commit comments