Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a368170

Browse files
tomflannaghandopplershift
authored andcommitted
Ensure streamplot Euler step is always called when going out of bounds. (#11947)
* FIX: Ensure Euler step is always called when going out of bounds. This makes figures neater. Also fix places that were using dmap.grid.nx for the width of the grid, which should be dmap.grid.nx - 1. * Remove unused code * Improve the OutOfBounds logic in the integration. Moves the raising to the gradient steps simplifying the integrator code a bit. Also makes OutOfBounds inherit from IndexError as suggested by tacaswell. * TST: push up the tolerance on mac We are seeing 1bit differences on 2 pixel for this test between linux/win vs mac.
1 parent 47e3aa5 commit a368170

20 files changed

+11107
-10509
lines changed

lib/matplotlib/streamplot.py

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
133133
line_kw['zorder'] = zorder
134134
arrow_kw['zorder'] = zorder
135135

136-
## Sanity checks.
136+
# Sanity checks.
137137
if u.shape != grid.shape or v.shape != grid.shape:
138138
raise ValueError("'u' and 'v' must match the shape of 'Grid(x, y)'")
139139

@@ -156,8 +156,8 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
156156

157157
# Check if start_points are outside the data boundaries
158158
for xs, ys in sp2:
159-
if not (grid.x_origin <= xs <= grid.x_origin + grid.width
160-
and grid.y_origin <= ys <= grid.y_origin + grid.height):
159+
if not (grid.x_origin <= xs <= grid.x_origin + grid.width and
160+
grid.y_origin <= ys <= grid.y_origin + grid.height):
161161
raise ValueError("Starting point ({}, {}) outside of data "
162162
"boundaries".format(xs, ys))
163163

@@ -263,8 +263,8 @@ def __init__(self, grid, mask):
263263
self.grid = grid
264264
self.mask = mask
265265
# Constants for conversion between grid- and mask-coordinates
266-
self.x_grid2mask = (mask.nx - 1) / grid.nx
267-
self.y_grid2mask = (mask.ny - 1) / grid.ny
266+
self.x_grid2mask = (mask.nx - 1) / (grid.nx - 1)
267+
self.y_grid2mask = (mask.ny - 1) / (grid.ny - 1)
268268

269269
self.x_mask2grid = 1. / self.x_grid2mask
270270
self.y_mask2grid = 1. / self.y_grid2mask
@@ -413,19 +413,21 @@ class TerminateTrajectory(Exception):
413413

414414

415415
# Integrator definitions
416-
#========================
416+
# =======================
417417

418418
def get_integrator(u, v, dmap, minlength, maxlength, integration_direction):
419419

420420
# rescale velocity onto grid-coordinates for integrations.
421421
u, v = dmap.data2grid(u, v)
422422

423423
# speed (path length) will be in axes-coordinates
424-
u_ax = u / dmap.grid.nx
425-
v_ax = v / dmap.grid.ny
424+
u_ax = u / (dmap.grid.nx - 1)
425+
v_ax = v / (dmap.grid.ny - 1)
426426
speed = np.ma.sqrt(u_ax ** 2 + v_ax ** 2)
427427

428428
def forward_time(xi, yi):
429+
if not dmap.grid.within_grid(xi, yi):
430+
raise OutOfBounds
429431
ds_dt = interpgrid(speed, xi, yi)
430432
if ds_dt == 0:
431433
raise TerminateTrajectory()
@@ -480,6 +482,10 @@ def integrate(x0, y0):
480482
return integrate
481483

482484

485+
class OutOfBounds(IndexError):
486+
pass
487+
488+
483489
def _integrate_rk12(x0, y0, dmap, f, maxlength):
484490
"""2nd-order Runge-Kutta algorithm with adaptive step size.
485491
@@ -523,18 +529,28 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength):
523529
xf_traj = []
524530
yf_traj = []
525531

526-
while dmap.grid.within_grid(xi, yi):
527-
xf_traj.append(xi)
528-
yf_traj.append(yi)
532+
while True:
529533
try:
534+
if dmap.grid.within_grid(xi, yi):
535+
xf_traj.append(xi)
536+
yf_traj.append(yi)
537+
else:
538+
raise OutOfBounds
539+
540+
# Compute the two intermediate gradients.
541+
# f should raise OutOfBounds if the locations given are
542+
# outside the grid.
530543
k1x, k1y = f(xi, yi)
531-
k2x, k2y = f(xi + ds * k1x,
532-
yi + ds * k1y)
533-
except IndexError:
534-
# Out of the domain on one of the intermediate integration steps.
535-
# Take an Euler step to the boundary to improve neatness.
536-
ds, xf_traj, yf_traj = _euler_step(xf_traj, yf_traj, dmap, f)
537-
stotal += ds
544+
k2x, k2y = f(xi + ds * k1x, yi + ds * k1y)
545+
546+
except OutOfBounds:
547+
# Out of the domain during this step.
548+
# Take an Euler step to the boundary to improve neatness
549+
# unless the trajectory is currently empty.
550+
if xf_traj:
551+
ds, xf_traj, yf_traj = _euler_step(xf_traj, yf_traj,
552+
dmap, f)
553+
stotal += ds
538554
break
539555
except TerminateTrajectory:
540556
break
@@ -546,7 +562,7 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength):
546562

547563
nx, ny = dmap.grid.shape
548564
# Error is normalized to the axes coordinates
549-
error = np.hypot((dx2 - dx1) / nx, (dy2 - dy1) / ny)
565+
error = np.hypot((dx2 - dx1) / (nx - 1), (dy2 - dy1) / (ny - 1))
550566

551567
# Only save step if within error tolerance
552568
if error < maxerror:
@@ -651,7 +667,6 @@ def _gen_starting_points(shape):
651667
x, y = 0, 0
652668
direction = 'right'
653669
for i in range(nx * ny):
654-
655670
yield x, y
656671

657672
if direction == 'right':
Binary file not shown.

0 commit comments

Comments
 (0)