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