Skip to content

Commit

Permalink
Add xfail_dask pytest marker; add masks in smoothing function tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rpmanser committed Aug 19, 2021
1 parent 0270d23 commit ee1c96b
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 51 deletions.
3 changes: 3 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ def array_type(request):
quantity = metpy.units.units.Quantity
if request.param == 'dask':
dask_array = pytest.importorskip('dask.array', reason='dask.array is not available')
marker = request.node.get_closest_marker('xfail_dask')
if marker is not None:
request.applymarker(pytest.mark.xfail(reason=marker.args[0]))
return lambda d, u, *, mask=None: quantity(dask_array.array(d), u)
elif request.param == 'xarray':
return lambda d, u, *, mask=None: xarray.DataArray(d, attrs={'units': u})
Expand Down
3 changes: 1 addition & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ extension = MPY = flake8_metpy:MetPyChecker
paths = ./tools/flake8-metpy

[tool:pytest]
# https://github.com/matplotlib/pytest-mpl/issues/69
markers = mpl_image_compare
markers = xfail_dask: marks tests as expected to fail with Dask arrays
norecursedirs = build docs .idea
doctest_optionflags = NORMALIZE_WHITESPACE
mpl-results-path = test_output
Expand Down
9 changes: 0 additions & 9 deletions src/metpy/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,4 @@ def wrapper(*args, **kwargs):
return wrapper
return dec


def xfail_dask(array, reason):
"""Xfail a test if `array` is a `dask.array.Array`."""
dask_array = pytest.importorskip('dask.array', reason='dask.array not available')

if not isinstance(array, xr.DataArray) and isinstance(array.m, dask_array.Array):
pytest.xfail(reason=reason)


check_and_silence_deprecation = check_and_silence_warning(MetpyDeprecationWarning)
89 changes: 49 additions & 40 deletions tests/calc/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
pressure_to_height_std, sigma_to_pressure, smooth_circular,
smooth_gaussian, smooth_n_point, smooth_rectangular, smooth_window,
wind_components, wind_direction, wind_speed, windchill)
from metpy.testing import (assert_almost_equal, assert_array_almost_equal, assert_array_equal,
xfail_dask)
from metpy.testing import assert_almost_equal, assert_array_almost_equal, assert_array_equal
from metpy.units import units


Expand Down Expand Up @@ -74,33 +73,31 @@ def test_speed(array_type):
assert_array_almost_equal(true_speed, speed, 4)


@pytest.mark.xfail_dask('Item assignment with <class "numpy.ndarray"> not supported')
def test_direction(array_type):
"""Test calculating wind direction."""
# The last two (u, v) pairs and their masks test masking calm and negative directions
mask = [False, True, False, True, True]
u = array_type([4., 2., 0., 0., 1.], 'm/s', mask=mask)
v = array_type([0., 2., 4., 0., -1], 'm/s', mask=mask)

xfail_dask(u, 'Item assignment with <class "numpy.ndarray"> not supported')

direc = wind_direction(u, v)

true_dir = array_type([270., 225., 180., 0., 315.], 'degree', mask=mask)

assert_array_almost_equal(true_dir, direc, 4)


@pytest.mark.xfail_dask('Boolean index assignment in Dask expects equally shaped arrays')
def test_direction_with_north_and_calm(array_type):
"""Test how wind direction handles northerly and calm winds."""
mask = [False, False, False, True, True]
u = array_type([0., -0., 0., 1., 1.], 'm/s', mask=mask)
v = array_type([0., 0., -5., 1., 1.], 'm/s', mask=mask)

xfail_dask(u, 'Boolean index assignment in Dask expects equally shaped arrays')
mask = [False, False, False, True]
u = array_type([0., -0., 0., 1.], 'm/s', mask=mask)
v = array_type([0., 0., -5., 1.], 'm/s', mask=mask)

direc = wind_direction(u, v)

true_dir = array_type([0., 0., 360., 225., 225.], 'deg', mask=mask)
true_dir = array_type([0., 0., 360., 225.], 'deg', mask=mask)

assert_array_almost_equal(true_dir, direc, 4)

Expand All @@ -111,14 +108,13 @@ def test_direction_dimensions():
assert str(d.units) == 'degree'


@pytest.mark.xfail_dask('Boolean index assignment in Dask expects equally shaped arrays')
def test_oceanographic_direction(array_type):
"""Test oceanographic direction (to) convention."""
mask = [False, True, False]
u = array_type([5., 5., 0.], 'm/s', mask=mask)
v = array_type([-5., 0., 5.], 'm/s', mask=mask)

xfail_dask(u, 'Boolean index assignment in Dask expects equally shaped arrays')

direc = wind_direction(u, v, convention='to')
true_dir = array_type([135., 90., 360.], 'deg', mask=mask)
assert_almost_equal(direc, true_dir, 4)
Expand Down Expand Up @@ -211,14 +207,13 @@ def test_windchill_face_level():
assert_array_almost_equal(wc, values, 0)


@pytest.mark.xfail_dask('operands could not be broadcast together with shapes (0, 5) (nan,)')
def test_heat_index_basic(array_type):
"""Test the basic heat index calculation."""
mask = [False, True, False, True, False]
temp = array_type([80, 88, 92, 110, 86], 'degF', mask=mask)
rh = array_type([40, 100, 70, 40, 88], 'percent', mask=mask)

xfail_dask(temp, 'operands could not be broadcast together with shapes (0, 5) (nan,)')

hi = heat_index(temp, rh)
values = array_type([80, 121, 112, 136, 104], 'degF', mask=mask)
assert_array_almost_equal(hi, values, 0)
Expand Down Expand Up @@ -413,6 +408,10 @@ def test_coriolis_units():
assert f.units == units('1/second')


@pytest.mark.xfail_dask(
'boolean index did not match indexed array along dimension 0; dimension is 2 but '
'corresponding boolean dimension is 3'
)
def test_apparent_temperature(array_type):
"""Test the apparent temperature calculation."""
temperature = array_type([[90, 90, 70],
Expand All @@ -422,12 +421,6 @@ def test_apparent_temperature(array_type):
wind = array_type([[5, 3, 3],
[10, 1, 10]], 'mph')

reason = (
'boolean index did not match indexed array along dimension 0; dimension is 2 but '
'corresponding boolean dimension is 3'
)
xfail_dask(temperature, reason)

truth = units.Quantity(np.ma.array([[99.6777178, 86.3357671, 70], [8.8140662, 20, 60]],
mask=[[False, False, True], [False, True, True]]),
units.degF)
Expand Down Expand Up @@ -557,11 +550,15 @@ def test_smooth_gaussian_3d_units():

def test_smooth_n_pt_5(array_type):
"""Test the smooth_n_pt function using 5 points."""
hght = array_type([[5640., 5640., 5640., 5640., 5640.],
[5684., 5676., 5666., 5659., 5651.],
[5728., 5712., 5692., 5678., 5662.],
[5772., 5748., 5718., 5697., 5673.],
[5816., 5784., 5744., 5716., 5684.]], '')
hght = np.array([[5640., 5640., 5640., 5640., 5640.],
[5684., 5676., 5666., 5659., 5651.],
[5728., 5712., 5692., 5678., 5662.],
[5772., 5748., 5718., 5697., 5673.],
[5816., 5784., 5744., 5716., 5684.]])
mask = np.zeros_like(hght)
mask[::2, ::2] = 1
hght = array_type(hght, '', mask=mask)

shght = smooth_n_point(hght, 5, 1)
s_true = array_type([[5640., 5640., 5640., 5640., 5640.],
[5684., 5675.75, 5666.375, 5658.875, 5651.],
Expand Down Expand Up @@ -692,11 +689,15 @@ def test_smooth_gaussian_temperature():

def test_smooth_window(array_type):
"""Test smooth_window with default configuration."""
hght = array_type([[5640., 5640., 5640., 5640., 5640.],
[5684., 5676., 5666., 5659., 5651.],
[5728., 5712., 5692., 5678., 5662.],
[5772., 5748., 5718., 5697., 5673.],
[5816., 5784., 5744., 5716., 5684.]], 'meter')
hght = np.array([[5640., 5640., 5640., 5640., 5640.],
[5684., 5676., 5666., 5659., 5651.],
[5728., 5712., 5692., 5678., 5662.],
[5772., 5748., 5718., 5697., 5673.],
[5816., 5784., 5744., 5716., 5684.]])
mask = np.zeros_like(hght)
mask[::2, ::2] = 1
hght = array_type(hght, 'meter', mask=mask)

smoothed = smooth_window(hght, np.array([[1, 0, 1], [0, 0, 0], [1, 0, 1]]))
truth = array_type([[5640., 5640., 5640., 5640., 5640.],
[5684., 5675., 5667.5, 5658.5, 5651.],
Expand Down Expand Up @@ -725,11 +726,15 @@ def test_smooth_window_1d_dataarray():

def test_smooth_rectangular(array_type):
"""Test smooth_rectangular with default configuration."""
hght = array_type([[5640., 5640., 5640., 5640., 5640.],
[5684., 5676., 5666., 5659., 5651.],
[5728., 5712., 5692., 5678., 5662.],
[5772., 5748., 5718., 5697., 5673.],
[5816., 5784., 5744., 5716., 5684.]], 'meter')
hght = np.array([[5640., 5640., 5640., 5640., 5640.],
[5684., 5676., 5666., 5659., 5651.],
[5728., 5712., 5692., 5678., 5662.],
[5772., 5748., 5718., 5697., 5673.],
[5816., 5784., 5744., 5716., 5684.]])
mask = np.zeros_like(hght)
mask[::2, ::2] = 1
hght = array_type(hght, 'meter', mask=mask)

smoothed = smooth_rectangular(hght, (5, 3))
truth = array_type([[5640., 5640., 5640., 5640., 5640.],
[5684., 5676., 5666., 5659., 5651.],
Expand All @@ -741,11 +746,15 @@ def test_smooth_rectangular(array_type):

def test_smooth_circular(array_type):
"""Test smooth_circular with default configuration."""
hght = array_type([[5640., 5640., 5640., 5640., 5640.],
[5684., 5676., 5666., 5659., 5651.],
[5728., 5712., 5692., 5678., 5662.],
[5772., 5748., 5718., 5697., 5673.],
[5816., 5784., 5744., 5716., 5684.]], 'meter')
hght = np.array([[5640., 5640., 5640., 5640., 5640.],
[5684., 5676., 5666., 5659., 5651.],
[5728., 5712., 5692., 5678., 5662.],
[5772., 5748., 5718., 5697., 5673.],
[5816., 5784., 5744., 5716., 5684.]])
mask = np.zeros_like(hght)
mask[::2, ::2] = 1
hght = array_type(hght, 'meter', mask=mask)

smoothed = smooth_circular(hght, 2, 2)
truth = array_type([[5640., 5640., 5640., 5640., 5640.],
[5684., 5676., 5666., 5659., 5651.],
Expand Down

0 comments on commit ee1c96b

Please sign in to comment.