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

Skip to content

Ensure streamplot Euler step is always called when going out of bounds. #11947

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 35 additions & 20 deletions lib/matplotlib/streamplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
line_kw['zorder'] = zorder
arrow_kw['zorder'] = zorder

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

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

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

Expand Down Expand Up @@ -263,8 +263,8 @@ def __init__(self, grid, mask):
self.grid = grid
self.mask = mask
# Constants for conversion between grid- and mask-coordinates
self.x_grid2mask = (mask.nx - 1) / grid.nx
self.y_grid2mask = (mask.ny - 1) / grid.ny
self.x_grid2mask = (mask.nx - 1) / (grid.nx - 1)
self.y_grid2mask = (mask.ny - 1) / (grid.ny - 1)

self.x_mask2grid = 1. / self.x_grid2mask
self.y_mask2grid = 1. / self.y_grid2mask
Expand Down Expand Up @@ -413,19 +413,21 @@ class TerminateTrajectory(Exception):


# Integrator definitions
#========================
# =======================

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

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

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

def forward_time(xi, yi):
if not dmap.grid.within_grid(xi, yi):
raise OutOfBounds
ds_dt = interpgrid(speed, xi, yi)
if ds_dt == 0:
raise TerminateTrajectory()
Expand Down Expand Up @@ -480,6 +482,10 @@ def integrate(x0, y0):
return integrate


class OutOfBounds(IndexError):
pass


def _integrate_rk12(x0, y0, dmap, f, maxlength):
"""2nd-order Runge-Kutta algorithm with adaptive step size.

Expand Down Expand Up @@ -523,18 +529,28 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength):
xf_traj = []
yf_traj = []

while dmap.grid.within_grid(xi, yi):
xf_traj.append(xi)
yf_traj.append(yi)
while True:
try:
if dmap.grid.within_grid(xi, yi):
xf_traj.append(xi)
yf_traj.append(yi)
else:
raise OutOfBounds

# Compute the two intermediate gradients.
# f should raise OutOfBounds if the locations given are
# outside the grid.
k1x, k1y = f(xi, yi)
k2x, k2y = f(xi + ds * k1x,
yi + ds * k1y)
except IndexError:
# Out of the domain on one of the intermediate integration steps.
# Take an Euler step to the boundary to improve neatness.
ds, xf_traj, yf_traj = _euler_step(xf_traj, yf_traj, dmap, f)
stotal += ds
k2x, k2y = f(xi + ds * k1x, yi + ds * k1y)

except OutOfBounds:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If all of the raising logic is here it might be clearer to just use if and else statements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The raising logic is all here directly within the try block, including line 529 as well (so can't simply be put in the else). I can rewrite it using a flag variable like so. What do you think? Happy to update if you prefer it.

        try:
            out_of_bounds = False
            if dmap.grid.within_grid(xi, yi):
                xf_traj.append(xi)
                yf_traj.append(yi)

                k1x, k1y = f(xi, yi)

                if dmap.grid.within_grid(xi + ds * k1x, yi + ds * k1y):
                    k2x, k2y = f(xi + ds * k1x, yi + ds * k1y)
                else:
                    out_of_bounds = True
            else:
                out_of_bounds = True

            if out_of_bounds:
                # Out of the domain during this step.
                # Take an Euler step to the boundary to improve neatness
                # unless the trajectory is currently empty.
                if xf_traj:
                    ds, xf_traj, yf_traj = _euler_step(xf_traj, yf_traj,
                                                       dmap, f)
                    stotal += ds
                break

        except TerminateTrajectory:
            break

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another simple approach could be to move the bounds check and raise OutOfbounds into f. Would simplify the logic in the interpolator.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed a change to the PR moving the raise OutOfBounds into f. I tried quite a few things and I think this is the clearest.

# Out of the domain during this step.
# Take an Euler step to the boundary to improve neatness
# unless the trajectory is currently empty.
if xf_traj:
ds, xf_traj, yf_traj = _euler_step(xf_traj, yf_traj,
dmap, f)
stotal += ds
break
except TerminateTrajectory:
break
Expand All @@ -546,7 +562,7 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength):

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

# Only save step if within error tolerance
if error < maxerror:
Expand Down Expand Up @@ -651,7 +667,6 @@ def _gen_starting_points(shape):
x, y = 0, 0
direction = 'right'
for i in range(nx * ny):

yield x, y

if direction == 'right':
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading