Skip to content
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

Implement gwfs function from GEMPAK #911

Merged
merged 12 commits into from
Aug 6, 2018

Conversation

sgdecker
Copy link
Contributor

@sgdecker sgdecker commented Aug 2, 2018

This PR adds the GEMPAK GWFS function by wrapping the SciPy function gaussian_filter. As far as I can tell, there is no way to make gaussian_filter act exactly identical to what GEMPAK does, but the result is awfully close:
gemgwfs
mp_gwfs

This is a work in progress. A few things I haven't figured out:

  • How to deal with units. This function returns ndarrays no matter what.
  • How to add this function to the MetPy API part of the docs.

jthielen and others added 3 commits August 1, 2018 15:41
Since NumPy 1.15 came out, we have been getting pages and pages of
warnings about our use of non-tuple based indexing. This commit converts
all of our previous list-based indexing to tuple-based indexing. This
doesn't yet clear out all the warnings (some of them are upstream in
Pint and scipy), but it does drop the number down from 600-something to
10-something.
This commit wraps the gaussian_filter function from SciPy so that it
emulates the GWFS function from GEMPAK as closely as possible. The key
difference is that GEMPAK adds the leftover weights from outside the
averaging window to the center point, but there is no option for
gaussian_filter to do this.

Left unfinished is restoring metadata to the result. For instance, an
xarray.DataArray provided as input becomes a lowly numpy.ndarray on
output.
This commit adds a few basic tests for the metpy.calc.gwfs function,
and also updates the GEMPAK compatibility table.

I'm not sure how to add new documentation to the MetPy API section.
-------
`pint.Quantity`
The filtered 2D scalar grid

Choose a reason for hiding this comment

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

W293 blank line contains whitespace


where N > 1. If N <= 1, N = 2 is assumed. For example, if N = 4,
then the 4 delta x wave length is passed with approximate response
1/e.

Choose a reason for hiding this comment

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

W291 trailing whitespace

n = int(round(n))
if n < 2:
n = 2
sgma = n / (2*np.pi)

Choose a reason for hiding this comment

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

E226 missing whitespace around arithmetic operator

if n < 2:
n = 2
sgma = n / (2*np.pi)
res = gaussian_filter(scalar_grid, sgma, truncate=2*np.sqrt(2))

Choose a reason for hiding this comment

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

E226 missing whitespace around arithmetic operator

res = gaussian_filter(scalar_grid, sgma, truncate=2*np.sqrt(2))
return res


Choose a reason for hiding this comment

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

W293 blank line contains whitespace

30.40077472, 41.40077472, 54.40077472, 69.31520047, 82.67898913],
[ 6.20489127, 7.39627081, 10.40077472, 15.40077472, 22.40077472,
31.40077472, 42.40077472, 55.40077472, 70.31520047, 83.67898913],
[ 7.20489127, 8.39627081, 11.40077472, 16.40077472, 23.40077472,

Choose a reason for hiding this comment

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

E201 whitespace after '['
E241 multiple spaces after ','

31.40077472, 42.40077472, 55.40077472, 70.31520047, 83.67898913],
[ 7.20489127, 8.39627081, 11.40077472, 16.40077472, 23.40077472,
32.40077472, 43.40077472, 56.40077472, 71.31520047, 84.67898913],
[ 8.20038736, 9.3917669, 12.39627081, 17.39627081, 24.39627081,

Choose a reason for hiding this comment

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

E201 whitespace after '['
E241 multiple spaces after ','

32.40077472, 43.40077472, 56.40077472, 71.31520047, 84.67898913],
[ 8.20038736, 9.3917669, 12.39627081, 17.39627081, 24.39627081,
33.39627081, 44.39627081, 57.39627081, 72.31069656, 85.67448522],
[ 9.00900782, 10.20038736, 13.20489127, 18.20489127, 25.20489127,

Choose a reason for hiding this comment

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

E201 whitespace after '['

34.20489127, 45.20489127, 58.20489127, 73.11931702, 86.48310568]])
assert_array_almost_equal(s, s_true)

def test_gwfs_small_n():

Choose a reason for hiding this comment

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

E302 expected 2 blank lines, found 1

[2.00708990, 3.01417981, 6.01417981, 11.0141798, 17.9503707],
[3.00708990, 4.01417981, 7.01417981, 12.0141798, 18.9503707],
[4.00000000, 5.00708990, 8.00708990, 13.0070899, 19.9432808]]
print(s-s_true)

Choose a reason for hiding this comment

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

E226 missing whitespace around arithmetic operator

Copy link
Member

@dopplershift dopplershift left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution, one more step towards GEMPAK!

My only real concern is with the naming of the function, especially in this case where I can't even figure out definitively what gwfs even stands for.

Also, you'll need to update metpy.calc.rst to include this function in the list of calculations in the docs.

if n < 2:
n = 2
sgma = n / (2 * np.pi)
res = gaussian_filter(scalar_grid, sgma, truncate=2 * np.sqrt(2))
Copy link
Member

Choose a reason for hiding this comment

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

Could just be return gaussian_filter(scalar_grid, sgma, truncate=2 * np.sqrt(2))

@@ -614,6 +615,90 @@ def sigma_to_pressure(sigma, psfc, ptop):
return sigma * (psfc - ptop) + ptop


@exporter.export
@preprocess_xarray
def gwfs(scalar_grid, n):
Copy link
Member

Choose a reason for hiding this comment

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

We need a name for this--replicating GEMPAK functionality is a big 👍 . Replicating naming from the 80's when you had to pay per-character (or so it seems) is something I'd like to not replicate. 😁

How about gaussian_smooth?

Also, is n the most sensible argument for this function? Personally, I'd like to be able to specify the kernel width in some kind of physical units as well, but we don't have to do this right now. If this is the most sensible interface to you the user, then I'm happy to just roll with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, gwfs is more of a placeholder name to make it clear what GEMPAK function it corresponds to. Looking ahead to eventual implementations of GEMPAK's sm5s and sm9s, maybe all of these should start with 'smooth', so smooth_gaussian, smooth_5pt, etc.?

Regarding n, it sounds like you are wanting the related GEMPAK function RDFS (Resolution Dependent Filter for Scalar), which takes dx instead of n as the second argument, and then behind the scenes computes an appropriate n based on dx and calls GWFS. But I suppose just because they are two separate GEMPAK functions doesn't mean they have to be separate MetPy functions.

Copy link
Member

Choose a reason for hiding this comment

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

smooth_gaussian sounds good to me.

If both GWFS and RDFS are doing the same calculation, just tweaking the parameter, I’d lean towards putting all of the functionality in smooth_gaussian, and just look at the arguments passed in to determine the proper course of action. That doesn’t have to be implemented in this PR, though—I’d be happy just to merge the GWFS equivalent.

@dopplershift
Copy link
Member

@kgoebber, you've written another version of this previously--I'd love to have your input on this and if it would do everything you need it to do.

@dopplershift dopplershift added Type: Feature New functionality Area: Calc Pertains to calculations labels Aug 2, 2018
@dopplershift dopplershift added this to the 0.9 milestone Aug 2, 2018
@dopplershift
Copy link
Member

Can you update the function to maintain the units of the input array? Then this PR would close #664 .

@kgoebber
Copy link
Collaborator

kgoebber commented Aug 2, 2018

I have just used the gaussian_filter - this is a nice warp to that which will present slightly less verbose syntax. gwfs likely stands for gaussian weighted filter scalar in GEMPAK parlance. Eventually we'll want to add the five point and nine point smoother too as they would likely be useful too, but not a top priority.

@dopplershift
Copy link
Member

(See #659 for the 5- and 9-point smoothers.)

@sgdecker
Copy link
Contributor Author

sgdecker commented Aug 3, 2018

I do want the function to maintain units, just need to figure it out. I'd also like to make sure it only smooths in the horizontal direction if given an array with more than two dimensions. This would match GEMPAK semantics, where even when you are creating a cross section or time series plot, calling GWFS as part of the function to plot smooths each vertical level or time independently.

@dopplershift
Copy link
Member

So to maintain units, what you want to do is:

if hasattr(scalar_grid, ‘units’):
    res = res * scalar_grid.units

Putting that block after the call to gaussian_filter and before the return.

This commit wraps the gaussian_filter function from SciPy so that it
emulates the GWFS function from GEMPAK as closely as possible. The key
difference is that GEMPAK adds the leftover weights from outside the
averaging window to the center point, but there is no option for
gaussian_filter to do this.

Left unfinished is restoring metadata to the result. For instance, an
xarray.DataArray provided as input becomes a lowly numpy.ndarray on
output.
This commit adds a few basic tests for the metpy.calc.gwfs function,
and also updates the GEMPAK compatibility table.

I'm not sure how to add new documentation to the MetPy API section.
gwfs is extended to preserve units and ensure that only horizontal
directions are smoothed (assuming last two axes are
horizontal). Documentation is updated so that this function appears.
One place was overlooked when changing gwfs() to
smooth_gaussian(). This corrects the oversight.
@sgdecker
Copy link
Contributor Author

sgdecker commented Aug 3, 2018

Thanks for the pointer on the units. I'll leave the alternate, dx version for a future issue..

@dopplershift dopplershift merged commit ef22931 into Unidata:master Aug 6, 2018
@dopplershift
Copy link
Member

Thanks for the contribution @sgdecker !

@zbruick zbruick mentioned this pull request Aug 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Calc Pertains to calculations Type: Feature New functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants