18
18
def streamplot (axes , x , y , u , v , density = 1 , linewidth = None , color = None ,
19
19
cmap = None , norm = None , arrowsize = 1 , arrowstyle = '-|>' ,
20
20
minlength = 0.1 , transform = None , zorder = None , start_points = None ,
21
- maxlength = 4.0 , integration_direction = 'both' ):
21
+ maxlength = 4.0 , integration_direction = 'both' , broken_streamlines = True ):
22
22
"""
23
23
Draw streamlines of a vector flow.
24
24
@@ -70,6 +70,9 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
70
70
Integrate the streamline in forward, backward or both directions.
71
71
data : indexable object, optional
72
72
DATA_PARAMETER_PLACEHOLDER
73
+ broken_streamlines : If False, forces streamlines to continue until they
74
+ leave the plot domain. If True, they may be terminated if they
75
+ come too close to another streamline. Default is True.
73
76
74
77
Returns
75
78
-------
@@ -149,7 +152,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
149
152
for xm , ym in _gen_starting_points (mask .shape ):
150
153
if mask [ym , xm ] == 0 :
151
154
xg , yg = dmap .mask2grid (xm , ym )
152
- t = integrate (xg , yg )
155
+ t = integrate (xg , yg , broken_streamlines )
153
156
if t is not None :
154
157
trajectories .append (t )
155
158
else :
@@ -177,7 +180,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
177
180
xg = np .clip (xg , 0 , grid .nx - 1 )
178
181
yg = np .clip (yg , 0 , grid .ny - 1 )
179
182
180
- t = integrate (xg , yg )
183
+ t = integrate (xg , yg , broken_streamlines )
181
184
if t is not None :
182
185
trajectories .append (t )
183
186
@@ -294,19 +297,19 @@ def data2grid(self, xd, yd):
294
297
def grid2data (self , xg , yg ):
295
298
return xg / self .x_data2grid , yg / self .y_data2grid
296
299
297
- def start_trajectory (self , xg , yg ):
300
+ def start_trajectory (self , xg , yg , broken_streamlines = True ):
298
301
xm , ym = self .grid2mask (xg , yg )
299
- self .mask ._start_trajectory (xm , ym )
302
+ self .mask ._start_trajectory (xm , ym , broken_streamlines )
300
303
301
304
def reset_start_point (self , xg , yg ):
302
305
xm , ym = self .grid2mask (xg , yg )
303
306
self .mask ._current_xy = (xm , ym )
304
307
305
- def update_trajectory (self , xg , yg ):
308
+ def update_trajectory (self , xg , yg , broken_streamlines = True ):
306
309
if not self .grid .within_grid (xg , yg ):
307
310
raise InvalidIndexError
308
311
xm , ym = self .grid2mask (xg , yg )
309
- self .mask ._update_trajectory (xm , ym )
312
+ self .mask ._update_trajectory (xm , ym , broken_streamlines )
310
313
311
314
def undo_trajectory (self ):
312
315
self .mask ._undo_trajectory ()
@@ -396,17 +399,17 @@ def __init__(self, density):
396
399
def __getitem__ (self , args ):
397
400
return self ._mask [args ]
398
401
399
- def _start_trajectory (self , xm , ym ):
402
+ def _start_trajectory (self , xm , ym , broken_streamlines = True ):
400
403
"""Start recording streamline trajectory"""
401
404
self ._traj = []
402
- self ._update_trajectory (xm , ym )
405
+ self ._update_trajectory (xm , ym , broken_streamlines )
403
406
404
407
def _undo_trajectory (self ):
405
408
"""Remove current trajectory from mask"""
406
409
for t in self ._traj :
407
410
self ._mask [t ] = 0
408
411
409
- def _update_trajectory (self , xm , ym ):
412
+ def _update_trajectory (self , xm , ym , broken_streamlines = True ):
410
413
"""
411
414
Update current trajectory position in mask.
412
415
@@ -418,7 +421,10 @@ def _update_trajectory(self, xm, ym):
418
421
self ._mask [ym , xm ] = 1
419
422
self ._current_xy = (xm , ym )
420
423
else :
421
- raise InvalidIndexError
424
+ if broken_streamlines :
425
+ raise InvalidIndexError
426
+ else :
427
+ pass
422
428
423
429
424
430
class InvalidIndexError (Exception ):
@@ -457,9 +463,9 @@ def backward_time(xi, yi):
457
463
dxi , dyi = forward_time (xi , yi )
458
464
return - dxi , - dyi
459
465
460
- def integrate (x0 , y0 ):
466
+ def integrate (x0 , y0 , broken_streamlines = True ):
461
467
"""
462
- Return (N, 2) grid-coordinates of trajectory based on starting point.
468
+ Return x, y grid-coordinates of trajectory based on starting point.
463
469
464
470
Integrate both forward and backward in time from starting point in
465
471
grid coordinates.
@@ -472,17 +478,17 @@ def integrate(x0, y0):
472
478
stotal , xy_traj = 0. , []
473
479
474
480
try :
475
- dmap .start_trajectory (x0 , y0 )
481
+ dmap .start_trajectory (x0 , y0 , broken_streamlines )
476
482
except InvalidIndexError :
477
483
return None
478
484
if integration_direction in ['both' , 'backward' ]:
479
- s , xyt = _integrate_rk12 (x0 , y0 , dmap , backward_time , maxlength )
485
+ s , xyt = _integrate_rk12 (x0 , y0 , dmap , backward_time , maxlength , broken_streamlines )
480
486
stotal += s
481
487
xy_traj += xyt [::- 1 ]
482
488
483
489
if integration_direction in ['both' , 'forward' ]:
484
490
dmap .reset_start_point (x0 , y0 )
485
- s , xyt = _integrate_rk12 (x0 , y0 , dmap , forward_time , maxlength )
491
+ s , xyt = _integrate_rk12 (x0 , y0 , dmap , forward_time , maxlength , broken_streamlines )
486
492
stotal += s
487
493
xy_traj += xyt [1 :]
488
494
@@ -508,7 +514,7 @@ class OutOfBounds(IndexError):
508
514
pass
509
515
510
516
511
- def _integrate_rk12 (x0 , y0 , dmap , f , maxlength ):
517
+ def _integrate_rk12 (x0 , y0 , dmap , f , maxlength , broken_streamlines = True ):
512
518
"""
513
519
2nd-order Runge-Kutta algorithm with adaptive step size.
514
520
@@ -588,7 +594,7 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength):
588
594
xi += dx2
589
595
yi += dy2
590
596
try :
591
- dmap .update_trajectory (xi , yi )
597
+ dmap .update_trajectory (xi , yi , broken_streamlines )
592
598
except InvalidIndexError :
593
599
break
594
600
if stotal + ds > maxlength :
0 commit comments