diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a8bc68e..f4be3811 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ ## Xarray-Spatial Changelog ----------- +### Version 0.5.0 - 2025-12-15 +- Python 3.14 is now supported! +- Fixed bug in curvature dask+cupy args and added unit test for curvature(#824) +- Added dask cupy test for slope func (#824) +- Added dask cupy test for aspect func (#824) +- Added in dask-cupy convolve_2d test (#823) +- Now ensures the hash value fits into an unsigned 64-bit integer for NumPy 2.0.0 (#805) +- Added dask and pyarrow to setup.cfg test area (#822) +- Allow Negative Target Height in Viewshed Analysis (viewshed.py) (#812) +- Small Fixes while testing Cuda 13 (#818) +- Support for certain Dask+Cupy (#815) +- Update docstring for viewshed (#807) + + ### Version 0.4.0 - 2024-04-25 - Python 3.12 is now supported! - Python 3.9 & 3.8 are no longer supported. diff --git a/README.md b/README.md index 7f7cc0f2..bbbffbda 100644 --- a/README.md +++ b/README.md @@ -208,10 +208,10 @@ In the GIS world, rasters are used for representing continuous phenomena (e.g. e | Name | NumPy xr.DataArray | Dask xr.DataArray | CuPy GPU xr.DataArray | Dask GPU xr.DataArray | |:----------:|:----------------------:|:--------------------:|:-------------------:|:------:| -| [Aspect](xrspatial/aspect.py) | ✅️ | ✅️ | ✅️ | ⚠️ | -| [Curvature](xrspatial/curvature.py) | ✅️ | | | ⚠️ | +| [Aspect](xrspatial/aspect.py) | ✅️ | ✅️ | ✅️ | ✅️ | +| [Curvature](xrspatial/curvature.py) | ✅️ |⚠️✅️ |⚠️✅️ | ⚠️✅️ | | [Hillshade](xrspatial/hillshade.py) | ✅️ | ✅️ | | | -| [Slope](xrspatial/slope.py) | ✅️ | ✅️ | ✅️ | ⚠️ | +| [Slope](xrspatial/slope.py) | ✅️ | ✅️ | ✅️ | ⚠️✅️ | | [Terrain Generation](xrspatial/terrain.py) | ✅️ | ✅️ | ✅️ | | | [Viewshed](xrspatial/viewshed.py) | ✅️ | | | | | [Perlin Noise](xrspatial/perlin.py) | ✅️ | ✅️ | ✅️ | | diff --git a/xrspatial/curvature.py b/xrspatial/curvature.py index fcec5650..3a1292a4 100644 --- a/xrspatial/curvature.py +++ b/xrspatial/curvature.py @@ -15,8 +15,7 @@ class cupy(object): from numba import cuda # local modules -from xrspatial.utils import (ArrayTypeFunctionMapping, cuda_args, get_dataarray_resolution, ngjit, - not_implemented_func) +from xrspatial.utils import (ArrayTypeFunctionMapping, cuda_args, get_dataarray_resolution, ngjit) @ngjit @@ -84,10 +83,14 @@ def _run_cupy(data: cupy.ndarray, return out + def _run_dask_cupy(data: da.Array, - cellsize: Union[int, float]) -> da.Array: + cellsize: Union[int, float]) -> da.Array: data = data.astype(cupy.float32) - _func = partial(_cpu, cellsize=cellsize) + cellsize_arr = cupy.array([float(cellsize)], dtype='f4') + + _func = partial(_run_cupy, cellsize=cellsize_arr) + out = data.map_overlap(_func, depth=(1, 1), boundary=cupy.nan, diff --git a/xrspatial/tests/general_checks.py b/xrspatial/tests/general_checks.py index b28da3db..f5f9bfba 100644 --- a/xrspatial/tests/general_checks.py +++ b/xrspatial/tests/general_checks.py @@ -123,7 +123,7 @@ def assert_numpy_equals_cupy(numpy_agg, cupy_agg, func, nan_edges=True, atol=0, numpy_result.data, cupy_result.data.get(), equal_nan=True, atol=atol, rtol=rtol) -def assert_numpy_equals_dask_cupy(numpy_agg, dask_cupy_agg, func, nan_edges=True): +def assert_numpy_equals_dask_cupy(numpy_agg, dask_cupy_agg, func, nan_edges=True, atol=0, rtol=1e-7): numpy_result = func(numpy_agg) if nan_edges: assert_nan_edges_effect(numpy_result) @@ -131,5 +131,5 @@ def assert_numpy_equals_dask_cupy(numpy_agg, dask_cupy_agg, func, nan_edges=True dask_cupy_result = func(dask_cupy_agg) general_output_checks(dask_cupy_agg, dask_cupy_result) np.testing.assert_allclose( - numpy_result.data, dask_cupy_result.data.compute().get(), equal_nan=True + numpy_result.data, dask_cupy_result.data.compute().get(), equal_nan=True, atol=atol, rtol=rtol ) diff --git a/xrspatial/tests/test_aspect.py b/xrspatial/tests/test_aspect.py index 25f0386a..39fb1cfb 100644 --- a/xrspatial/tests/test_aspect.py +++ b/xrspatial/tests/test_aspect.py @@ -2,7 +2,9 @@ import pytest from xrspatial import aspect -from xrspatial.tests.general_checks import (assert_nan_edges_effect, assert_numpy_equals_cupy, +from xrspatial.tests.general_checks import (assert_nan_edges_effect, + assert_numpy_equals_cupy, + assert_numpy_equals_dask_cupy, assert_numpy_equals_dask_numpy, create_test_raster, cuda_and_cupy_available, general_output_checks) @@ -75,3 +77,13 @@ def test_numpy_equals_cupy_random_data(random_data): numpy_agg = create_test_raster(random_data, backend='numpy') cupy_agg = create_test_raster(random_data, backend='cupy') assert_numpy_equals_cupy(numpy_agg, cupy_agg, aspect, atol=1e-6, rtol=1e-6) + + +@cuda_and_cupy_available +@pytest.mark.parametrize("size", [(2, 4), (10, 15)]) +@pytest.mark.parametrize( + "dtype", [np.int32, np.int64, np.uint32, np.uint64, np.float32, np.float64]) +def test_numpy_equals_dask_cupy_random_data(random_data): + numpy_agg = create_test_raster(random_data, backend='numpy') + dask_cupy_agg = create_test_raster(random_data, backend='dask+cupy') + assert_numpy_equals_dask_cupy(numpy_agg, dask_cupy_agg, aspect, atol=1e-6, rtol=1e-6) diff --git a/xrspatial/tests/test_curvature.py b/xrspatial/tests/test_curvature.py index 2a914dd0..d10c18ca 100644 --- a/xrspatial/tests/test_curvature.py +++ b/xrspatial/tests/test_curvature.py @@ -3,6 +3,7 @@ from xrspatial import curvature from xrspatial.tests.general_checks import (assert_numpy_equals_cupy, + assert_numpy_equals_dask_cupy, assert_numpy_equals_dask_numpy, create_test_raster, cuda_and_cupy_available, general_output_checks) @@ -87,9 +88,6 @@ def test_numpy_equals_cupy_random_data(random_data): numpy_agg = create_test_raster(random_data, backend='numpy') cupy_agg = create_test_raster(random_data, backend='cupy') assert_numpy_equals_cupy(numpy_agg, cupy_agg, curvature) - # NOTE: Dask + GPU code paths don't currently work because of - # dask casting cupy arrays to numpy arrays during - # https://github.com/dask/dask/issues/4842 @pytest.mark.parametrize("size", [(2, 4), (10, 15)]) @@ -99,3 +97,13 @@ def test_numpy_equals_dask_random_data(random_data): numpy_agg = create_test_raster(random_data, backend='numpy') dask_agg = create_test_raster(random_data, backend='dask') assert_numpy_equals_dask_numpy(numpy_agg, dask_agg, curvature) + + +@cuda_and_cupy_available +@pytest.mark.parametrize("size", [(2, 4), (10, 15)]) +@pytest.mark.parametrize( + "dtype", [np.int32, np.int64, np.uint32, np.uint64, np.float32, np.float64]) +def test_numpy_equals_dask_cupy_random_data(random_data): + numpy_agg = create_test_raster(random_data, backend='numpy') + dask_cupy_agg = create_test_raster(random_data, backend='dask+cupy') + assert_numpy_equals_dask_cupy(numpy_agg, dask_cupy_agg, curvature, atol=1e-6, rtol=1e-6) diff --git a/xrspatial/tests/test_slope.py b/xrspatial/tests/test_slope.py index 235b2df1..df043f4f 100644 --- a/xrspatial/tests/test_slope.py +++ b/xrspatial/tests/test_slope.py @@ -3,6 +3,7 @@ from xrspatial import slope from xrspatial.tests.general_checks import (assert_nan_edges_effect, assert_numpy_equals_cupy, + assert_numpy_equals_dask_cupy, assert_numpy_equals_dask_numpy, create_test_raster, cuda_and_cupy_available, general_output_checks) @@ -63,3 +64,13 @@ def test_numpy_equals_cupy_qgis_data(elevation_raster): numpy_agg = input_data(elevation_raster, 'numpy') cupy_agg = input_data(elevation_raster, 'cupy') assert_numpy_equals_cupy(numpy_agg, cupy_agg, slope) + + +@cuda_and_cupy_available +@pytest.mark.parametrize("size", [(2, 4), (10, 15)]) +@pytest.mark.parametrize( + "dtype", [np.int32, np.int64, np.uint32, np.uint64, np.float32, np.float64]) +def test_numpy_equals_dask_cupy_random_data(random_data): + numpy_agg = create_test_raster(random_data, backend='numpy') + dask_cupy_agg = create_test_raster(random_data, backend='dask+cupy') + assert_numpy_equals_dask_cupy(numpy_agg, dask_cupy_agg, slope, atol=1e-6, rtol=1e-6)