@@ -19,7 +19,8 @@ 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
21
maxlength = 4.0 , integration_direction = 'both' ,
22
- broken_streamlines = True , * , num_arrows = 1 ):
22
+ broken_streamlines = True , * , integration_max_step_scale = 1.0 ,
23
+ integration_max_error_scale = 1.0 , num_arrows = 1 ):
23
24
"""
24
25
Draw streamlines of a vector flow.
25
26
@@ -73,6 +74,26 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
73
74
If False, forces streamlines to continue until they
74
75
leave the plot domain. If True, they may be terminated if they
75
76
come too close to another streamline.
77
+ integration_max_step_scale : float, default: 1.0
78
+ Multiplier on the maximum allowable step in the streamline integration routine.
79
+ A value between zero and one results in a max integration step smaller than
80
+ the default max step, resulting in more accurate streamlines at the cost
81
+ of greater computation time. A value greater than one results in a max
82
+ integration step larger than the default value, reducing computation time
83
+ at the cost of less accurate streamlines. Must be greater than zero.
84
+
85
+ .. versionadded:: 3.11
86
+
87
+ integration_max_error_scale : float, default: 1.0
88
+ Multiplier on the maximum allowable error in the streamline integration routine.
89
+ A value between zero and one results in a tighter max integration error than
90
+ the default max error, resulting in more accurate streamlines at the cost
91
+ of greater computation time. A value greater than one results in a looser
92
+ max integration error than the default value, reducing computation time at
93
+ the cost of less accurate streamlines. Must be greater than zero.
94
+
95
+ .. versionadded:: 3.11
96
+
76
97
num_arrows : int
77
98
Number of arrows per streamline. The arrows are spaced equally along the steps
78
99
each streamline takes. Note that this can be different to being spaced equally
@@ -97,6 +118,18 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
97
118
mask = StreamMask (density )
98
119
dmap = DomainMap (grid , mask )
99
120
121
+ if integration_max_step_scale <= 0.0 :
122
+ raise ValueError (
123
+ "The value of integration_max_step_scale must be > 0, " +
124
+ f"got { integration_max_step_scale } "
125
+ )
126
+
127
+ if integration_max_error_scale <= 0.0 :
128
+ raise ValueError (
129
+ "The value of integration_max_error_scale must be > 0, " +
130
+ f"got { integration_max_error_scale } "
131
+ )
132
+
100
133
if num_arrows < 0 :
101
134
raise ValueError (f"The value of num_arrows must be >= 0, got { num_arrows = } " )
102
135
@@ -160,7 +193,9 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
160
193
for xm , ym in _gen_starting_points (mask .shape ):
161
194
if mask [ym , xm ] == 0 :
162
195
xg , yg = dmap .mask2grid (xm , ym )
163
- t = integrate (xg , yg , broken_streamlines )
196
+ t = integrate (xg , yg , broken_streamlines ,
197
+ integration_max_step_scale ,
198
+ integration_max_error_scale )
164
199
if t is not None :
165
200
trajectories .append (t )
166
201
else :
@@ -188,7 +223,8 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
188
223
xg = np .clip (xg , 0 , grid .nx - 1 )
189
224
yg = np .clip (yg , 0 , grid .ny - 1 )
190
225
191
- t = integrate (xg , yg , broken_streamlines )
226
+ t = integrate (xg , yg , broken_streamlines , integration_max_step_scale ,
227
+ integration_max_error_scale )
192
228
if t is not None :
193
229
trajectories .append (t )
194
230
@@ -481,7 +517,8 @@ def backward_time(xi, yi):
481
517
dxi , dyi = forward_time (xi , yi )
482
518
return - dxi , - dyi
483
519
484
- def integrate (x0 , y0 , broken_streamlines = True ):
520
+ def integrate (x0 , y0 , broken_streamlines = True , integration_max_step_scale = 1.0 ,
521
+ integration_max_error_scale = 1.0 ):
485
522
"""
486
523
Return x, y grid-coordinates of trajectory based on starting point.
487
524
@@ -501,14 +538,18 @@ def integrate(x0, y0, broken_streamlines=True):
501
538
return None
502
539
if integration_direction in ['both' , 'backward' ]:
503
540
s , xyt = _integrate_rk12 (x0 , y0 , dmap , backward_time , maxlength ,
504
- broken_streamlines )
541
+ broken_streamlines ,
542
+ integration_max_step_scale ,
543
+ integration_max_error_scale )
505
544
stotal += s
506
545
xy_traj += xyt [::- 1 ]
507
546
508
547
if integration_direction in ['both' , 'forward' ]:
509
548
dmap .reset_start_point (x0 , y0 )
510
549
s , xyt = _integrate_rk12 (x0 , y0 , dmap , forward_time , maxlength ,
511
- broken_streamlines )
550
+ broken_streamlines ,
551
+ integration_max_step_scale ,
552
+ integration_max_error_scale )
512
553
stotal += s
513
554
xy_traj += xyt [1 :]
514
555
@@ -525,7 +566,9 @@ class OutOfBounds(IndexError):
525
566
pass
526
567
527
568
528
- def _integrate_rk12 (x0 , y0 , dmap , f , maxlength , broken_streamlines = True ):
569
+ def _integrate_rk12 (x0 , y0 , dmap , f , maxlength , broken_streamlines = True ,
570
+ integration_max_step_scale = 1.0 ,
571
+ integration_max_error_scale = 1.0 ):
529
572
"""
530
573
2nd-order Runge-Kutta algorithm with adaptive step size.
531
574
@@ -551,7 +594,7 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength, broken_streamlines=True):
551
594
# This error is below that needed to match the RK4 integrator. It
552
595
# is set for visual reasons -- too low and corners start
553
596
# appearing ugly and jagged. Can be tuned.
554
- maxerror = 0.003
597
+ maxerror = 0.003 * integration_max_error_scale
555
598
556
599
# This limit is important (for all integrators) to avoid the
557
600
# trajectory skipping some mask cells. We could relax this
@@ -560,6 +603,7 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength, broken_streamlines=True):
560
603
# nature of the interpolation, this doesn't boost speed by much
561
604
# for quite a bit of complexity.
562
605
maxds = min (1. / dmap .mask .nx , 1. / dmap .mask .ny , 0.1 )
606
+ maxds *= integration_max_step_scale
563
607
564
608
ds = maxds
565
609
stotal = 0
0 commit comments