-
Notifications
You must be signed in to change notification settings - Fork 421
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
Add gradient and derivatives for irregular grids #692
Conversation
metpy/calc/tools.py
Outdated
|
||
|
||
def first_derivative(): | ||
a = 'foobar' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
F841 local variable 'a' is assigned to but never used
metpy/calc/tools.py
Outdated
|
||
def first_derivative(): | ||
a = 'foobar' | ||
return b |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
F821 undefined name 'b'
W292 no newline at end of file
It doesn't break Sphinx, but it does break PyCharm. Just get rid of it since it's not necessary.
For some reason pyplot needs to be imported explicitly. Easy enough just to fix.
They were assuming they always got Quantities.
metpy/calc/tests/test_tools.py
Outdated
"""Test lapacian with full 2D arrays.""" | ||
laplac_true = 2 * (np.ones_like(deriv_2d_data.f) * (deriv_2d_data.a + deriv_2d_data.b)) | ||
laplac = laplacian(deriv_2d_data.f, x=(deriv_2d_data.y, deriv_2d_data.x)) | ||
assert_array_almost_equal(laplac, laplac_true , 5) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E203 whitespace before ','
metpy/calc/tools.py
Outdated
combined_delta = delta[delta_slice0] + delta[delta_slice1] | ||
delta_diff = delta[delta_slice1] - delta[delta_slice0] | ||
center = (- delta[delta_slice1] / (combined_delta * delta[delta_slice0]) * f[slice0] | ||
+ delta_diff / (delta[delta_slice0] * delta[delta_slice1]) * f[slice1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W503 line break before binary operator
metpy/calc/tools.py
Outdated
delta_diff = delta[delta_slice1] - delta[delta_slice0] | ||
center = (- delta[delta_slice1] / (combined_delta * delta[delta_slice0]) * f[slice0] | ||
+ delta_diff / (delta[delta_slice0] * delta[delta_slice1]) * f[slice1] | ||
+ delta[delta_slice0] / (combined_delta * delta[delta_slice1]) * f[slice2]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W503 line break before binary operator
metpy/calc/tools.py
Outdated
combined_delta = delta[delta_slice0] + delta[delta_slice1] | ||
big_delta = combined_delta + delta[delta_slice0] | ||
left = (- big_delta / (combined_delta * delta[delta_slice0]) * f[slice0] | ||
+ combined_delta / (delta[delta_slice0] * delta[delta_slice1]) * f[slice1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W503 line break before binary operator
metpy/calc/tools.py
Outdated
big_delta = combined_delta + delta[delta_slice0] | ||
left = (- big_delta / (combined_delta * delta[delta_slice0]) * f[slice0] | ||
+ combined_delta / (delta[delta_slice0] * delta[delta_slice1]) * f[slice1] | ||
- delta[delta_slice0] / (combined_delta * delta[delta_slice1]) * f[slice2]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W503 line break before binary operator
metpy/calc/tools.py
Outdated
combined_delta = delta[delta_slice0] + delta[delta_slice1] | ||
center = 2 * (f[slice0] / (combined_delta * delta[delta_slice0]) | ||
- f[slice1] / (delta[delta_slice0] * delta[delta_slice1]) | ||
+ f[slice2] / (combined_delta * delta[delta_slice1])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W503 line break before binary operator
metpy/calc/tools.py
Outdated
|
||
combined_delta = delta[delta_slice0] + delta[delta_slice1] | ||
left = 2 * (f[slice0] / (combined_delta * delta[delta_slice0]) | ||
- f[slice1] / (delta[delta_slice0] * delta[delta_slice1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W503 line break before binary operator
metpy/calc/tools.py
Outdated
combined_delta = delta[delta_slice0] + delta[delta_slice1] | ||
left = 2 * (f[slice0] / (combined_delta * delta[delta_slice0]) | ||
- f[slice1] / (delta[delta_slice0] * delta[delta_slice1]) | ||
+ f[slice2] / (combined_delta * delta[delta_slice1])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W503 line break before binary operator
metpy/calc/tools.py
Outdated
|
||
combined_delta = delta[delta_slice0] + delta[delta_slice1] | ||
right = 2 * (f[slice0] / (combined_delta * delta[delta_slice0]) | ||
- f[slice1] / (delta[delta_slice0] * delta[delta_slice1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W503 line break before binary operator
metpy/calc/tools.py
Outdated
combined_delta = delta[delta_slice0] + delta[delta_slice1] | ||
right = 2 * (f[slice0] / (combined_delta * delta[delta_slice0]) | ||
- f[slice1] / (delta[delta_slice0] * delta[delta_slice1]) | ||
+ f[slice2] / (combined_delta * delta[delta_slice1])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W503 line break before binary operator
16c5524
to
7a6b93f
Compare
Need to find some way to make Code Climate less noisy--but I think we can ignore it here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few things that should probably be at least thought about - overall a huge improvement to our functionality!
return units.Quantity(ret, orig_units[0]) | ||
return [units.Quantity(m, u) for m, u in zip(ret, orig_units)] | ||
if orig_units[0] is not None: | ||
return units.Quantity(ret, orig_units[0]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that we just saw some weirdness in attaching units this way in #619 - maybe go with ret * orig_units[0]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that I don't understand what went wrong on that one, I'm not inclined to fix it proactively. I'd much prefer to have an actual test case to validate that the new way would be the right one. Either way outside the scope of this PR.
return units.Quantity(ret, orig_units[0]) | ||
else: | ||
return ret | ||
return [units.Quantity(m, u) if u is not None else m for m, u in zip(ret, orig_units)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above
return units.Quantity(ret, orig_units[0]) | ||
return [units.Quantity(m, u) for m, u in zip(ret, orig_units)] | ||
if orig_units[0] is not None: | ||
return units.Quantity(ret, orig_units[0]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again
return units.Quantity(ret, orig_units[0]) | ||
else: | ||
return ret | ||
return [units.Quantity(m, u) if u is not None else m for m, u in zip(ret, orig_units)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again
n, axis, delta = _process_deriv_args(f, kwargs) | ||
|
||
# create slice objects --- initially all are [:, :, ..., :] | ||
slice0 = [slice(None)] * n |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that these 10-ish lines are duplicated in the first derivative, should they be in a helper somewhere? Trivial lines, but more to maintain/bugfix and makes the functions even longer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about this, but they each have their own set of 5 slices that would have to be passed in--that seemed like it wouldn't simplify enough to justify.
|
||
|
||
def _process_gradient_args(kwargs): | ||
"""Handle common processing of arguments for gradient and gradient-like functions.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be an exception raised when a user through accident or mis-understanding passes both instead of just grabbing deltas
and ignoring x
silently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough.
if f.shape[axis] < 3: | ||
raise ValueError('f must have at least 3 point along the desired axis.') | ||
|
||
if 'delta' in kwargs: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar question here on silently ignoring an argument if both are passed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
metpy/calc/tools.py
Outdated
return n, axis, delta | ||
|
||
|
||
def _diff(x, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be in units like atleast_1d
and be more publicly accessible? I could see it being handy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meh, ok.
return dudx - dvdy | ||
|
||
|
||
@exporter.export | ||
@deprecated('0.7', addendum=' Use stretching_deformation and/or shearing_deformation instead.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should also probably note in the docs like we do elsewhere:
.. deprecated:: 0.6.0
Function has been moved to the Siphon library and will be removed from MetPy in 0.8.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I missed that we did that. Will fix.
Adds first and second derivatives, as well as gradient and laplacian functions. These work on grids that have uneven spacing between points.
The original, actually shipped name was `h_convergence`, not `convergence`.
With the new derivative functions, there's no performance benefit to the combined function. Instead of adding the appropriate named version, just deprecate the old one entirely in favor of the individual vorticity and divergence functions.
This will no longer give us any performance benefit, so remove it.
Use our own first_derivative and gradient functions. Some tests needed to be updated to account for changes to how values at the edge of the grid are treated.
metpy/calc/tools.py
Outdated
delta = _broadcast_to_axis(delta, axis, n) | ||
elif 'x' in kwargs: | ||
x = _broadcast_to_axis(kwargs['x'], axis, n) | ||
delta = _diff(x, axis=axis) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
F821 undefined name '_diff'
metpy/calc/tools.py
Outdated
raise ValueError('Must specify either "x" or "delta" for value positions.') | ||
|
||
return n, axis, delta | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W391 blank line at end of file
Other issues are already updated for deprecations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
It's just a nice helper for better error messages, so we can just backport as a noop.
It'd be great to get something like this into numpy |
|
||
import functools | ||
import warnings | ||
|
||
import numpy as np | ||
try: | ||
from numpy.core.numeric import normalize_axis_index |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should really be np.core.multiarray
instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes more sense, good to know. I'll look at a PR to numpy some time when I have some cycles...
Not sure what I'd call the damned thing 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactoring np.gradient
to use your _first_derivative
would be a good first step
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a pretty steep performance penalty (or it would seem from the complexity) for the support for irregular spacing though...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have irregular spacing - all we don't currently support is broadcasting of dx against the values.
This should be good to go now. This adds 1st and 2nd derivatives, as well as gradient and laplacian that work with irregular grids. This also updates the kinematics functions to use them rather than wrappers for numpy's gradient function.
Closes #174 .