diff --git a/.claude/sweep-api-consistency-state.csv b/.claude/sweep-api-consistency-state.csv index 42448932e..2a7b4ccc9 100644 --- a/.claude/sweep-api-consistency-state.csv +++ b/.claude/sweep-api-consistency-state.csv @@ -3,7 +3,7 @@ focal,2026-05-29,2689,HIGH,1;2;3;4,"Sweep 2026-05-29 (deep-sweep-api-consistency geotiff,2026-05-18,2106,MEDIUM,3,"Sweep 2026-05-18 (deep-sweep-api-consistency-geotiff-2026-05-18-1779164255). 1 MEDIUM Cat 3 finding fixed in this branch: open_geotiff(max_cloud_bytes=...) was the only kwarg on the public reader/writer surface without a Python type annotation. Docstring already declared ``int or None``; the surface and the docs disagreed. Fix adds ``int | None`` to the annotation; default stays the module-internal _MAX_CLOUD_BYTES_SENTINEL. Regression test in test_open_geotiff_max_cloud_bytes_annot_2106.py pins the immediate gap and parametrises over every public reader/writer to catch future ungenerated annotations. Prior sweep findings (#1922/#1935 kwarg ordering, #2052 mask_nodata parity, #2097 GPU MinIsWhite, #2095 zero-band 3D writes, #1946 write_vrt path/vrt_path shim) all confirmed fixed. Cross-sibling return-type drift (Cat 2): write_vrt returns str while to_geotiff and write_geotiff_gpu return path which is str | BinaryIO -- inspected and still LOW (callers do not substitute writers; the return-type drift is documented in each writer's docstring). Cross-cutting cross-module drift (chunk_size in reproject vs chunks in geotiff; target_crs vs crs) documented but not filed per sweep template (cross-cutting). cuda-validated." hydro-d8,2026-05-29,2709,HIGH,1;5,"Sweep 2026-05-29 (deep-sweep-api-consistency-hydro-d8-2026-05-29). Scope = the 13 D8-variant files only; dinf/mfd read for reference but not modified. 1 HIGH Cat 1 + 1 MEDIUM Cat 5 fixed in this branch (#2709, PR #2716). HIGH Cat 1: stream_order_d8 named its strahler/shreve selector `ordering` while sibling stream_order_dinf/stream_order_mfd use `method`; both names live in the public API and the __init__.py _StreamOrderDispatch special-cases the drift (translates ordering->method for non-d8). Fix adds `method` as an accepted alias on stream_order_d8 (case-insensitive; takes precedence; conflicting ordering+method raises ValueError), keeping `ordering` working so the out-of-scope dispatcher (passes ordering=) and existing callers are unaffected. Full rename to `method` deferred because deprecating `ordering` would warn on every stream_order(routing='d8') call via the dispatcher I cannot touch in this scope. MEDIUM Cat 5: basins_d8 (watershed_d8.py) is a backward-compat wrapper whose docstring said 'use basin instead' but emitted no warning; added DeprecationWarning(stacklevel=2). Tests added for alias parity/precedence/conflict/case-insensitivity and for the basins_d8 warning. Findings documented but NOT filed per template: (LOW Cat 1 cross-module, out of scope) dinf siblings name the first arg `flow_dir_dinf` (stream_link/flow_path/hand/watershed_dinf) while all D8 funcs use the cleaner `flow_dir`; D8 is the better convention so no D8 change -- the drift lives in the dinf files. (LOW Cat 4 defensive-validation drift) hand_d8 validates np.isfinite(threshold) but stream_link_d8/stream_order_d8 (same threshold: float = 100 param) do not; not user-facing signature surprise, document only. No Cat 2 return drift (every D8 public fn returns xr.DataArray with coords/dims/attrs preserved; Dataset in -> Dataset out via @supports_dataset). No Cat 3 missing-hints beyond fill_d8 z_limit (optional, no hint) which mirrors its sibling style. All 13 D8 funcs are re-exported in xrspatial/hydro/__init__.py (no orphan API). cuda-validated: CUDA_AVAILABLE=True on this host; method-alias parity smoke-tested on a cupy DataArray. CI: ubuntu/windows/3.12 GitHub Actions green; macOS-3.14 + ReadTheDocs slow but no failures. NOTE: the /review-pr review comment could not be posted to GitHub (auto-mode permission denial on gh pr review); review findings were applied to code instead (case-insensitive conflict check + str|None hint, commit f8467320)." polygonize,2026-05-19,2148,HIGH,1;3,"Sweep 2026-05-19 (deep-sweep-api-consistency-polygonize-2026-05-19). 1 MEDIUM Cat 3 finding fixed in this branch (#2148): polygonize() was the only public vector/raster conversion function without a return type annotation. Sieve/contours/rasterize/clip_polygon all declare one. Fix adds a Union return annotation (numpy tuple | awkward tuple | geopandas GeoDataFrame | spatialpandas GeoDataFrame | geojson dict) using TYPE_CHECKING forward refs for optional deps, and expands the docstring Returns section to enumerate the per-return_type shapes. 1 HIGH Cat 1 finding NOT fixed in this PR -- cross-module rename: polygonize uses `connectivity` (int 4|8) while sieve uses `neighborhood` (int 4|8) for the identical rook/queen pixel-connectivity concept. Industry convention (GDAL, rasterio.features.sieve) favours `connectivity`; the deprecation shim belongs in sieve.py, not polygonize, so this is out of scope for the polygonize-scoped sweep branch. Documented here for the next sieve sweep pass. 1 LOW Cat 1 cross-cutting: polygonize/sieve/clip_polygon use `raster` while contours and many older modules use `agg` for the input DataArray -- library-wide drift, not filed per-module per sweep template. Cat 2 return-shape: polygonize returns tuple/GeoDataFrame/dict by return_type; consistent with contours' tuple/GeoDataFrame dispatch. No Cat 4 (no mutable defaults; connectivity=4 default matches sieve neighborhood=4 default). No Cat 5 (polygonize re-exported in xrspatial/__init__.py; no orphan API; no __all__ but consistent with module convention). cuda-validated: cupy backend accepts identical kwargs, smoke-tested with cupy DataArray on host with CUDA_AVAILABLE." -rasterize,2026-05-21,2250,MEDIUM,3,"Sweep 2026-05-21 (deep-sweep-api-consistency-rasterize-2026-05-21). 1 MEDIUM Cat 3 finding fixed in this branch (#2250): rasterize() was missing type annotations on geometries, columns, and merge (3 of 16 public params); the other 13 plus the return type were annotated. The docstring already declared the intended types so this was a doc-vs-signature drift. Fix annotates geometries: Any (because the accepted GeoDataFrame / dask_geopandas / iterable union spans optional deps), columns: Optional[Sequence[str]], merge: Union[str, Callable]. Regression test in test_rasterize_signature_annot_2250.py pins every param + the return annotation so a future contributor can't silently drop annotations again. Cross-module drift documented but not filed per template: clip_polygon(nodata) vs rasterize(fill) same concept different name; clip_polygon(name: Optional[str]=None) vs rasterize(name: str='rasterize') default convention; polygonize(column_name) vs rasterize(column) column selector. No Cat 1 in-module rename, no Cat 2 return drift (returns xr.DataArray as documented), no Cat 4 mutable defaults, no Cat 5 orphan API (rasterize is the only public symbol from the module and is re-exported in __init__). cuda-validated: cupy backend accepts identical kwargs, smoke-tested with use_cuda=True on host with CUDA_AVAILABLE." +rasterize,2026-06-09,3089,HIGH,1,"Sweep 2026-06-09 (deep-sweep-api-consistency-rasterize-2026-06-09). 1 HIGH Cat 1 fixed in this branch (#3089): rasterize(use_cuda=) vs open_geotiff(gpu=) named the identical GPU-backend opt-in differently; these are the only two public entry points with an explicit GPU boolean (no input array to dispatch on; both pair it with chunks= for dask) and both names were live in the public API at once. Fix renames the positional param to gpu (same slot, positional callers unaffected) and appends use_cuda=None as a deprecated alias: DeprecationWarning on use, TypeError when combined with gpu=True. Docstring, GPU merge warning text, CuPy ImportError text, and polygon_clip.py's internal dask+cupy caller updated (guarded so a legacy use_cuda in rasterize_kw does not collide with the new default); all rasterize test call sites migrated to gpu=; regression tests in test_rasterize_gpu_alias_3089.py pin slot position, warning, TypeError, backend parity, and the warning-free clip_polygon path. Re-inspection after the 2026-05-21 pass (#2250); prior cross-module notes (clip_polygon nodata vs fill, name default drift, polygonize column_name vs column) still documented-only. Docstring/signature parity verified programmatically (17/17 params, order matches). New params since last pass (check_crs, max_pixels) consistent with geotiff naming (max_pixels matches geotiff's). No Cat 2/4/5 findings. LOW noted, not fixed (other module's docs): docs/source/user_guide/focal.ipynb claims convolve_2d takes use_cuda, which it does not. cuda-validated: CUDA_AVAILABLE=True; numpy/cupy/dask+numpy/dask+cupy smoke-tested with identical kwargs, values equal." reproject,2026-05-29,2613,MEDIUM,1,"Sweep 2026-05-29 (deep-sweep-api-consistency-reproject-2026-05-29). 1 MEDIUM Cat 1 finding fixed in this branch (#2613, PR #2626): reproject() spelled the source/target concept two ways in one signature -- source_crs/target_crs (full words) for horizontal CRS but src_vertical_crs/tgt_vertical_crs (abbreviated) for the vertical datum. Renamed the vertical kwargs to source_vertical_crs/target_vertical_crs with a deprecation shim: old names still accepted, emit DeprecationWarning, and passing both old+new for one side raises TypeError. Docstring updated; existing vertical-shift tests migrated to new names; added back-compat + conflict tests. Verified on numpy AND cupy entry points (shared signature; backend dispatch is internal). Other findings documented but NOT filed per template: (LOW Cat 1) itrf_transform(src=/tgt=) uses abbreviated keyword-only names for ITRF frame names vs source_crs/target_crs elsewhere -- separate function family (frames, not CRS), left as-is. (LOW cross-cutting Cat 1) first-arg `raster` (reproject)/`rasters` (merge) vs `agg` in terrain modules -- library-wide drift, not per-module. Prior #1570 vertical_crs EPSG-int collision confirmed still fixed. No Cat 2 return drift (reproject/merge both return DataArray as documented; geoid_height scalar/array and itrf_transform tuple are distinct families). No Cat 4 default drift (resampling/transform_precision/chunk_size/bounds_policy/model defaults consistent across siblings). No Cat 5 orphan API (itrf_frames is list_frames aliased in __all__; vertical/itrf funcs namespaced under xrspatial.reproject like geotiff's funcs). cuda-validated: CUDA_AVAILABLE=True on this host." resample,2026-05-27,2544,MEDIUM,3,"Sweep 2026-05-27 (deep-sweep-api-consistency-resample-2026-05-27). 1 MEDIUM Cat 3 finding fixed in this branch (#2544): resample() was the only public symbol in xrspatial.resample without type annotations on any parameter or return; siblings slope/aspect/hillshade/curvature all annotate `agg: xr.DataArray` and `-> xr.DataArray`. Fix adds annotations matching the docstring (agg: xr.DataArray; scale_factor / target_resolution: float | tuple[float, float] | None; method: str; nodata: float | None; name: str) and a `-> xr.DataArray` return type, plus a docstring note that the @supports_dataset decorator accepts Dataset too. Regression test test_resample_signature_annot_2544.py pins every param and the return annotation. Other findings documented but not filed per template: (MEDIUM Cat 1 cross-module) `method` (resample) vs `resampling` (reproject/merge) -- same conceptual parameter, different name, cross-cutting rename, needs design issue. (LOW Cat 1 cross-cutting) first-arg `agg` (resample/slope/aspect/...) vs `raster` (reproject/rasterize/polygonize/sieve) -- library-wide drift, not per-module. (LOW Cat 5) ALL_METHODS imported by tests but not in __all__ (module has no __all__); borderline orphan but used for test parametrisation only. No Cat 2 (returns xr.DataArray as documented). No Cat 4 mutable defaults. resample is exported in xrspatial/__init__.py. cuda-validated: cupy backend smoke-tested with nearest, bilinear, and average on host with CUDA_AVAILABLE=True." slope,2026-05-29,2681,MEDIUM,3,"Sweep 2026-05-29 (deep-sweep-api-consistency-slope-2026-05-29). 1 MEDIUM Cat 3 finding fixed in this branch (#2681, PR #2687): slope() annotated name as `str` while every terrain-family sibling (aspect/northness/eastness in aspect.py, curvature in curvature.py) uses Optional[str]. name flows into xr.DataArray(name=name) which accepts None, so slope(agg, name=None) already worked at runtime -- the annotation was just wrong and inconsistent. Fix widens to Optional[str] and imports Optional (module previously imported only Union). Non-breaking (type-hint widening), no deprecation shim. Added test_name_annotation_matches_terrain_family (pins parity vs the 4 siblings via get_type_hints, unwrapping @supports_dataset) and test_name_none_accepted (slope(agg, name=None).name is None). Full test_slope.py passes (43). No backend logic touched -- numpy/cupy/dask+numpy/dask+cupy paths unchanged; public signature is shared across backends via ArrayTypeFunctionMapping. Other categories: no Cat 1 in-module rename (slope/aspect share identical public param names agg/name/method/z_unit/boundary); no Cat 2 return drift (returns xr.DataArray/Dataset via @supports_dataset, same coords/dims/attrs convention as siblings); no Cat 4 default drift (name/method='planar'/z_unit='meter'/boundary='nan' match across the family); no Cat 5 orphan API (slope re-exported in __init__.py, documented, no __all__ but consistent with module convention). Cross-cutting (documented, not filed per template): first-arg `agg` (slope/aspect/curvature) vs `raster` (reproject/rasterize/polygonize) is library-wide drift. cuda-validated: CUDA_AVAILABLE=True on this host; cupy slope smoke-tested (planar) and signature parity confirmed between numpy and cupy entry points." diff --git a/benchmarks/benchmarks/rasterize.py b/benchmarks/benchmarks/rasterize.py index 01f0cec1e..5a44a0776 100644 --- a/benchmarks/benchmarks/rasterize.py +++ b/benchmarks/benchmarks/rasterize.py @@ -97,11 +97,11 @@ def setup(self, nx, backend): self.bounds = (-180, -90, 180, 90) self.width = nx self.height = ny - self.use_cuda = (backend == "cupy") + self.gpu = (backend == "cupy") def time_rasterize_polygons(self, nx, backend): rasterize(self.pairs, width=self.width, height=self.height, - bounds=self.bounds, fill=0, use_cuda=self.use_cuda) + bounds=self.bounds, fill=0, gpu=self.gpu) class RasterizeComplexPolygons: @@ -118,11 +118,11 @@ def setup(self, nx, backend): self.bounds = (-180, -90, 180, 90) self.width = nx self.height = ny - self.use_cuda = (backend == "cupy") + self.gpu = (backend == "cupy") def time_rasterize_complex_polygons(self, nx, backend): rasterize(self.pairs, width=self.width, height=self.height, - bounds=self.bounds, fill=0, use_cuda=self.use_cuda) + bounds=self.bounds, fill=0, gpu=self.gpu) # ------------------------------------------------------------------------- @@ -142,11 +142,11 @@ def setup(self, nx, backend): self.bounds = (-180, -90, 180, 90) self.width = nx self.height = ny - self.use_cuda = (backend == "cupy") + self.gpu = (backend == "cupy") def time_rasterize_lines(self, nx, backend): rasterize(self.pairs, width=self.width, height=self.height, - bounds=self.bounds, fill=0, use_cuda=self.use_cuda) + bounds=self.bounds, fill=0, gpu=self.gpu) # ------------------------------------------------------------------------- @@ -166,11 +166,11 @@ def setup(self, nx, backend): self.bounds = (-180, -90, 180, 90) self.width = nx self.height = ny - self.use_cuda = (backend == "cupy") + self.gpu = (backend == "cupy") def time_rasterize_points(self, nx, backend): rasterize(self.pairs, width=self.width, height=self.height, - bounds=self.bounds, fill=0, use_cuda=self.use_cuda) + bounds=self.bounds, fill=0, gpu=self.gpu) # ------------------------------------------------------------------------- @@ -195,11 +195,11 @@ def setup(self, nx, backend): self.bounds = (-180, -90, 180, 90) self.width = nx self.height = ny - self.use_cuda = (backend == "cupy") + self.gpu = (backend == "cupy") def time_rasterize_mixed(self, nx, backend): rasterize(self.pairs, width=self.width, height=self.height, - bounds=self.bounds, fill=0, use_cuda=self.use_cuda) + bounds=self.bounds, fill=0, gpu=self.gpu) # ------------------------------------------------------------------------- @@ -248,8 +248,8 @@ def setup(self, n_polys, backend): vals.append(float(i + 1)) self.pairs = list(zip(geoms, vals)) self.bounds = (-180, -90, 180, 90) - self.use_cuda = (backend == "cupy") + self.gpu = (backend == "cupy") def time_rasterize_scaling(self, n_polys, backend): rasterize(self.pairs, width=1000, height=500, - bounds=self.bounds, fill=0, use_cuda=self.use_cuda) + bounds=self.bounds, fill=0, gpu=self.gpu) diff --git a/xrspatial/polygon_clip.py b/xrspatial/polygon_clip.py index 3338c9129..d4fec6fcb 100644 --- a/xrspatial/polygon_clip.py +++ b/xrspatial/polygon_clip.py @@ -213,7 +213,11 @@ def clip_polygon( rc, cc = raster.data.chunks[-2], raster.data.chunks[-1] kw.setdefault('chunks', (rc[0], cc[0])) if has_cuda_and_cupy() and is_dask_cupy(raster): - kw.setdefault('use_cuda', True) + # Respect a legacy ``use_cuda`` passed via rasterize_kw -- + # defaulting ``gpu`` as well would make rasterize() see both + # names and raise. + if 'use_cuda' not in kw: + kw.setdefault('gpu', True) mask = rasterize(geom_pairs, **kw) diff --git a/xrspatial/rasterize.py b/xrspatial/rasterize.py index b2d805c98..1e6451618 100644 --- a/xrspatial/rasterize.py +++ b/xrspatial/rasterize.py @@ -3121,7 +3121,7 @@ def rasterize( fill: float = np.nan, dtype: Optional[np.dtype] = None, all_touched: bool = False, - use_cuda: bool = False, + gpu: bool = False, name: str = 'rasterize', resolution: Optional[Union[float, Tuple[float, float]]] = None, like: Optional[xr.DataArray] = None, @@ -3129,6 +3129,7 @@ def rasterize( chunks: Optional[Union[int, Tuple[int, int]]] = None, max_pixels: int = MAX_PIXELS_DEFAULT, check_crs: bool = True, + use_cuda: Optional[bool] = None, ) -> xr.DataArray: """Rasterize vector geometries into a 2D DataArray. @@ -3206,8 +3207,10 @@ def rasterize( pixel-for-pixel up to rasterization tie-breaking on shared edges. If False, only pixels whose centers fall inside a polygon are burned. - use_cuda : bool, default False - If True, use the CuPy/CUDA backend. + gpu : bool, default False + If True, use the CuPy/CUDA backend. Same convention as + ``open_geotiff(gpu=True)``; combine with ``chunks`` for the + dask+cupy backend. name : str, default 'rasterize' Name for the output DataArray. resolution : float or (x_res, y_res), optional @@ -3247,7 +3250,7 @@ def rasterize( Custom merge function (pass a callable): For CPU backends, pass a ``@ngjit``-decorated function. For GPU - backends (``use_cuda=True``), pass a + backends (``gpu=True``), pass a ``@numba.cuda.jit(device=True)`` function. Signature:: merge_fn(pixel, props, is_first) -> float64 @@ -3258,7 +3261,7 @@ def rasterize( .. warning:: - On the GPU backends (``use_cuda=True``, with or without + On the GPU backends (``gpu=True``, with or without ``chunks``) a custom callable does not use CUDA atomics. Its per-pixel update is a non-atomic read-modify-write, so when geometries overlap, several threads may update the same pixel @@ -3268,14 +3271,14 @@ def rasterize( ``'max'``, ``'first'``, ``'last'``) do use atomics and stay deterministic over overlap; pass one of those if you need a stable result where geometries overlap on the GPU. Calling - ``rasterize`` with a callable ``merge`` and ``use_cuda=True`` + ``rasterize`` with a callable ``merge`` and ``gpu=True`` emits a ``UserWarning`` to this effect. chunks : int or (int, int), optional If given, use the dask backend and split the output raster into tiles of this size ``(row_chunk, col_chunk)``. Both axes must be ``> 0``. A single int uses the same chunk size for both axes. - Combined with ``use_cuda`` to select dask+numpy vs dask+cupy. + Combined with ``gpu`` to select dask+numpy vs dask+cupy. max_pixels : int, default 1_000_000_000 Safety cap on the resolved output size (``width * height``). The function raises ``ValueError`` before any host or device @@ -3292,6 +3295,10 @@ def rasterize( them yourself to match the template. Pass ``check_crs=False`` to skip the comparison (the output still inherits the template CRS). The check is a no-op when either side lacks a CRS. + use_cuda : bool, optional + Deprecated alias for ``gpu``; emits a ``DeprecationWarning`` + when passed. Passing both ``gpu=True`` and ``use_cuda`` raises + ``TypeError``. Returns ------- @@ -3317,6 +3324,22 @@ def rasterize( >>> density = rasterize(gdf, width=100, height=100, ... column='pop', merge='sum', fill=0) """ + # Deprecation shim: ``use_cuda`` was renamed to ``gpu`` so the GPU + # opt-in matches ``open_geotiff(gpu=True)`` (issue #3089). The old + # keyword still works but warns; asking for both is ambiguous. + if use_cuda is not None: + if gpu: + raise TypeError( + "rasterize() got both 'gpu' and its deprecated alias " + "'use_cuda'; pass only 'gpu'") + warnings.warn( + "rasterize(use_cuda=...) is deprecated; use gpu=... instead " + "(same convention as open_geotiff).", + DeprecationWarning, + stacklevel=2, + ) + gpu = use_cuda + # Fail early with a clear message if the optional ``vector`` extra # (shapely) is not installed, rather than deep inside a helper. _require_shapely() @@ -3611,10 +3634,10 @@ def rasterize( # ``_ensure_gpu_kernels``; ``None`` falls back to the non-atomic # closure path used for user callables. gpu_merge_name = None - if use_cuda: + if gpu: if cupy is None: raise ImportError( - "CuPy is required for use_cuda=True but is not installed") + "CuPy is required for gpu=True but is not installed") gpu_fns = _get_gpu_merge_fns() if isinstance(_merge_fn_gpu, str): gpu_merge_fn, should_write_gpu = gpu_fns[_merge_fn_gpu] @@ -3634,7 +3657,7 @@ def rasterize( # merge stays deterministic over overlap. warnings.warn( "A custom callable merge on the GPU backend " - "(use_cuda=True) uses a non-atomic read-modify-write, so " + "(gpu=True) uses a non-atomic read-modify-write, so " "values for pixels where geometries overlap are " "nondeterministic and may not match the CPU backend. Use a " "built-in string merge ('sum', 'count', 'min', 'max', " @@ -3647,7 +3670,7 @@ def rasterize( if chunks is not None: row_chunks, col_chunks = _normalize_chunks( chunks, final_height, final_width) - if use_cuda: + if gpu: out = _run_dask_cupy( geom_list, props_array, final_bounds, final_height, final_width, fill, final_dtype, @@ -3659,7 +3682,7 @@ def rasterize( final_height, final_width, fill, final_dtype, all_touched, merge_fn, should_write_cpu, row_chunks, col_chunks) - elif use_cuda: + elif gpu: out = _run_cupy(geom_list, props_array, final_bounds, final_height, final_width, fill, final_dtype, all_touched, gpu_merge_fn, should_write_gpu, diff --git a/xrspatial/tests/test_rasterize.py b/xrspatial/tests/test_rasterize.py index b9927214e..54d8b0b45 100644 --- a/xrspatial/tests/test_rasterize.py +++ b/xrspatial/tests/test_rasterize.py @@ -848,24 +848,24 @@ class TestCuPy: def test_cupy_output_type(self): geom = box(1, 1, 4, 4) result = rasterize([(geom, 1.0)], width=5, height=5, - bounds=(0, 0, 5, 5), use_cuda=True) + bounds=(0, 0, 5, 5), gpu=True) assert isinstance(result.data, cupy.ndarray) def test_cupy_matches_numpy(self): geom = box(1, 1, 8, 8) np_result = rasterize([(geom, 3.0)], width=10, height=10, - bounds=(0, 0, 10, 10), use_cuda=False) + bounds=(0, 0, 10, 10), gpu=False) cp_result = rasterize([(geom, 3.0)], width=10, height=10, - bounds=(0, 0, 10, 10), use_cuda=True) + bounds=(0, 0, 10, 10), gpu=True) np.testing.assert_array_equal( np_result.values, cupy.asnumpy(cp_result.data)) def test_cupy_multiple_polygons(self): pairs = [(box(0, 0, 4, 4), 1.0), (box(6, 6, 10, 10), 2.0)] np_result = rasterize(pairs, width=10, height=10, - bounds=(0, 0, 10, 10), use_cuda=False) + bounds=(0, 0, 10, 10), gpu=False) cp_result = rasterize(pairs, width=10, height=10, - bounds=(0, 0, 10, 10), use_cuda=True) + bounds=(0, 0, 10, 10), gpu=True) np.testing.assert_array_equal( np_result.values, cupy.asnumpy(cp_result.data)) @@ -874,9 +874,9 @@ def test_cupy_with_hole(self): hole = [(3, 3), (3, 7), (7, 7), (7, 3), (3, 3)] poly = Polygon(exterior, [hole]) np_result = rasterize([(poly, 1.0)], width=10, height=10, - bounds=(0, 0, 10, 10), use_cuda=False) + bounds=(0, 0, 10, 10), gpu=False) cp_result = rasterize([(poly, 1.0)], width=10, height=10, - bounds=(0, 0, 10, 10), use_cuda=True) + bounds=(0, 0, 10, 10), gpu=True) np.testing.assert_array_equal( np_result.values, cupy.asnumpy(cp_result.data)) @@ -887,9 +887,9 @@ def test_cupy_points_match_numpy(self): (MultiPoint([(0.5, 0.5), (4.5, 4.5)]), 3.0), ] np_result = rasterize(pairs, width=5, height=5, - bounds=(0, 0, 5, 5), fill=0, use_cuda=False) + bounds=(0, 0, 5, 5), fill=0, gpu=False) cp_result = rasterize(pairs, width=5, height=5, - bounds=(0, 0, 5, 5), fill=0, use_cuda=True) + bounds=(0, 0, 5, 5), fill=0, gpu=True) np.testing.assert_array_equal( np_result.values, cupy.asnumpy(cp_result.data)) @@ -902,9 +902,9 @@ def test_cupy_lines_match_numpy(self): (MultiLineString([[(0.5, 2.5), (4.5, 2.5)]]), 3.0), ] np_result = rasterize(pairs, width=10, height=10, - bounds=(0, 0, 5, 5), fill=0, use_cuda=False) + bounds=(0, 0, 5, 5), fill=0, gpu=False) cp_result = rasterize(pairs, width=10, height=10, - bounds=(0, 0, 5, 5), fill=0, use_cuda=True) + bounds=(0, 0, 5, 5), fill=0, gpu=True) np.testing.assert_array_equal( np_result.values, cupy.asnumpy(cp_result.data)) @@ -915,14 +915,14 @@ def test_cupy_mixed_types_match_numpy(self): (Point(2.5, 2.5), 3.0), ] np_result = rasterize(pairs, width=5, height=5, - bounds=(0, 0, 5, 5), fill=0, use_cuda=False) + bounds=(0, 0, 5, 5), fill=0, gpu=False) cp_result = rasterize(pairs, width=5, height=5, - bounds=(0, 0, 5, 5), fill=0, use_cuda=True) + bounds=(0, 0, 5, 5), fill=0, gpu=True) np.testing.assert_array_equal( np_result.values, cupy.asnumpy(cp_result.data)) def test_cupy_no_cupy_raises(self): - """use_cuda=True without cupy should raise ImportError.""" + """gpu=True without cupy should raise ImportError.""" # This test only runs if cupy IS available, so we just verify # the function works -- the ImportError path is tested by # the fact that the guard exists. @@ -1149,7 +1149,7 @@ def my_sum_gpu(pixel, props, is_first): bounds=(0, 0, 10, 10), fill=0, merge='sum') custom = rasterize(pairs, width=10, height=10, bounds=(0, 0, 10, 10), fill=0, - merge=my_sum_gpu, use_cuda=True) + merge=my_sum_gpu, gpu=True) np.testing.assert_array_equal( builtin.values, self._to_numpy(custom)) @@ -1169,7 +1169,7 @@ def my_sum_gpu(pixel, props, is_first): bounds=(0, 0, 10, 10), fill=0, merge='sum') custom = rasterize(pairs, width=10, height=10, bounds=(0, 0, 10, 10), fill=0, - merge=my_sum_gpu, use_cuda=True, + merge=my_sum_gpu, gpu=True, chunks=(5, 5)) np.testing.assert_array_equal( builtin.values, self._to_numpy(custom)) @@ -1437,7 +1437,7 @@ def test_polygon_parity(self, chunks): bounds=(0, 0, 10, 10)) dk_result = rasterize([(geom, 3.0)], width=10, height=10, bounds=(0, 0, 10, 10), - use_cuda=True, chunks=chunks) + gpu=True, chunks=chunks) assert isinstance(dk_result.data, da.Array) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1449,7 +1449,7 @@ def test_multiple_polygons_parity(self, chunks): bounds=(0, 0, 10, 10)) dk_result = rasterize(pairs, width=10, height=10, bounds=(0, 0, 10, 10), - use_cuda=True, chunks=chunks) + gpu=True, chunks=chunks) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1463,7 +1463,7 @@ def test_mixed_types_parity(self): bounds=(0, 0, 5, 5), fill=0) dk_result = rasterize(pairs, width=5, height=5, bounds=(0, 0, 5, 5), fill=0, - use_cuda=True, chunks=(3, 3)) + gpu=True, chunks=(3, 3)) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1473,7 +1473,7 @@ def test_chunk_boundary_polygon(self): bounds=(0, 0, 10, 10), fill=0) dk_result = rasterize([(geom, 5.0)], width=10, height=10, bounds=(0, 0, 10, 10), fill=0, - use_cuda=True, chunks=(5, 5)) + gpu=True, chunks=(5, 5)) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1484,7 +1484,7 @@ def test_line_parity(self, chunks): bounds=(0, 0, 10, 10), fill=0) dk_result = rasterize([(line, 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), fill=0, - use_cuda=True, chunks=chunks) + gpu=True, chunks=chunks) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1495,7 +1495,7 @@ def test_point_parity(self, chunks): bounds=(0, 0, 10, 10), fill=0) dk_result = rasterize(pairs, width=10, height=10, bounds=(0, 0, 10, 10), fill=0, - use_cuda=True, chunks=chunks) + gpu=True, chunks=chunks) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1505,7 +1505,7 @@ def test_chunk_boundary_line(self): bounds=(0, 0, 10, 10), fill=0) dk_result = rasterize([(line, 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), fill=0, - use_cuda=True, chunks=(5, 5)) + gpu=True, chunks=(5, 5)) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1515,7 +1515,7 @@ def test_chunk_boundary_point_on_edge(self): bounds=(0, 0, 10, 10), fill=0) dk_result = rasterize([(pt, 9.0)], width=10, height=10, bounds=(0, 0, 10, 10), fill=0, - use_cuda=True, chunks=(5, 5)) + gpu=True, chunks=(5, 5)) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1523,7 +1523,7 @@ def test_empty_tiles_are_fill(self): geom = box(0, 0, 2, 2) result = rasterize([(geom, 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), fill=-999, - use_cuda=True, chunks=(5, 5)) + gpu=True, chunks=(5, 5)) vals = self._to_numpy(result) assert np.all(vals[0:5, 5:10] == -999) @@ -1533,7 +1533,7 @@ def test_single_chunk_matches_numpy(self): bounds=(0, 0, 5, 5), fill=0) dk_result = rasterize([(geom, 2.0)], width=5, height=5, bounds=(0, 0, 5, 5), fill=0, - use_cuda=True, chunks=(100, 100)) + gpu=True, chunks=(100, 100)) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1541,7 +1541,7 @@ def test_output_is_dask_array(self): geom = box(0, 0, 5, 5) result = rasterize([(geom, 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), - use_cuda=True, chunks=(5, 5)) + gpu=True, chunks=(5, 5)) assert isinstance(result.data, da.Array) def test_output_shape_and_coords(self): @@ -1550,7 +1550,7 @@ def test_output_shape_and_coords(self): bounds=(0, 0, 10, 10)) dk_result = rasterize([(geom, 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), - use_cuda=True, chunks=(5, 5)) + gpu=True, chunks=(5, 5)) assert dk_result.shape == np_result.shape assert dk_result.dims == np_result.dims np.testing.assert_allclose( @@ -1562,7 +1562,7 @@ def test_compute_returns_cupy(self): geom = box(0, 0, 5, 5) result = rasterize([(geom, 1.0)], width=5, height=5, bounds=(0, 0, 5, 5), - use_cuda=True, chunks=(3, 3)) + gpu=True, chunks=(3, 3)) computed = result.compute() assert type(computed.data).__module__.startswith('cupy') @@ -1576,7 +1576,7 @@ def test_merge_mode_parity(self, merge_mode): dk_result = rasterize(pairs, width=10, height=10, bounds=(0, 0, 10, 10), fill=0, merge=merge_mode, - use_cuda=True, chunks=(5, 5)) + gpu=True, chunks=(5, 5)) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1588,13 +1588,13 @@ def test_polygon_with_hole(self): bounds=(0, 0, 10, 10), fill=0) dk_result = rasterize([(poly, 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), fill=0, - use_cuda=True, chunks=(5, 5)) + gpu=True, chunks=(5, 5)) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) def test_empty_geometry_list(self): result = rasterize([], width=5, height=5, bounds=(0, 0, 5, 5), - use_cuda=True, chunks=(3, 3)) + gpu=True, chunks=(3, 3)) assert isinstance(result.data, da.Array) vals = self._to_numpy(result) assert np.all(np.isnan(vals)) @@ -1607,7 +1607,7 @@ def test_all_touched_parity(self): dk_result = rasterize([(geom, 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), fill=0, all_touched=True, - use_cuda=True, chunks=(5, 5)) + gpu=True, chunks=(5, 5)) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1615,7 +1615,7 @@ def test_dtype_preserved(self): geom = box(0, 0, 5, 5) result = rasterize([(geom, 1.0)], width=5, height=5, bounds=(0, 0, 5, 5), dtype=np.float32, - use_cuda=True, chunks=(3, 3)) + gpu=True, chunks=(3, 3)) assert result.dtype == np.float32 def test_int_chunks_shorthand(self): @@ -1624,7 +1624,7 @@ def test_int_chunks_shorthand(self): bounds=(0, 0, 5, 5), fill=0) dk_result = rasterize([(geom, 1.0)], width=5, height=5, bounds=(0, 0, 5, 5), fill=0, - use_cuda=True, chunks=3) + gpu=True, chunks=3) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1639,7 +1639,7 @@ def test_geodataframe_with_chunks(self): bounds=(0, 0, 10, 10), column='value') dk_result = rasterize(gdf, width=10, height=10, bounds=(0, 0, 10, 10), column='value', - use_cuda=True, chunks=(5, 5)) + gpu=True, chunks=(5, 5)) np.testing.assert_array_equal( np_result.values, self._to_numpy(dk_result)) @@ -1937,7 +1937,7 @@ def test_like_attrs_propagated_dask(self): def test_like_attrs_propagated_cupy(self): like = _make_like(attrs={'crs': 'EPSG:32610'}) result = rasterize( - [(box(2, 2, 8, 8), 1.0)], like=like, fill=0, use_cuda=True, + [(box(2, 2, 8, 8), 1.0)], like=like, fill=0, gpu=True, ) assert result.attrs.get('crs') == 'EPSG:32610' assert result.attrs.get('_FillValue') == 0 @@ -1948,7 +1948,7 @@ def test_like_attrs_propagated_dask_cupy(self): like = _make_like(attrs={'crs': 'EPSG:32610'}) result = rasterize( [(box(2, 2, 8, 8), 1.0)], - like=like, fill=0, use_cuda=True, chunks=5, + like=like, fill=0, gpu=True, chunks=5, ) assert result.attrs.get('crs') == 'EPSG:32610' assert result.attrs.get('_FillValue') == 0 @@ -2162,7 +2162,7 @@ def test_bounds_override_strips_stale_grid_attrs_cupy(self): result = rasterize( [(box(20, 20, 80, 80), 1.0)], like=like, bounds=(0, 0, 100, 100), - width=10, height=10, fill=0, use_cuda=True, + width=10, height=10, fill=0, gpu=True, ) assert 'res' not in result.attrs assert 'transform' not in result.attrs @@ -2175,7 +2175,7 @@ def test_bounds_override_strips_stale_grid_attrs_dask_cupy(self): result = rasterize( [(box(20, 20, 80, 80), 1.0)], like=like, bounds=(0, 0, 100, 100), - width=10, height=10, fill=0, use_cuda=True, chunks=5, + width=10, height=10, fill=0, gpu=True, chunks=5, ) assert 'res' not in result.attrs assert 'transform' not in result.attrs @@ -2343,8 +2343,8 @@ def test_dask_numpy_ascending_matches_descending(self): @skip_no_cuda def test_cupy_ascending_matches_descending(self): geom = [(box(0, 0, 1, 1), 1.0)] - r_desc = rasterize(geom, like=_like_2170(False), fill=0, use_cuda=True) - r_asc = rasterize(geom, like=_like_2170(True), fill=0, use_cuda=True) + r_desc = rasterize(geom, like=_like_2170(False), fill=0, gpu=True) + r_asc = rasterize(geom, like=_like_2170(True), fill=0, gpu=True) # CuPy DataArrays expose .data.get() per project notes desc_vals = r_desc.data.get() if hasattr(r_desc.data, 'get') \ else r_desc.values @@ -2362,10 +2362,10 @@ def test_dask_cupy_ascending_matches_descending(self): geom = [(box(0, 0, 1, 1), 1.0)] r_desc = rasterize( geom, like=_like_2170(False), fill=0, - use_cuda=True, chunks=2).compute() + gpu=True, chunks=2).compute() r_asc = rasterize( geom, like=_like_2170(True), fill=0, - use_cuda=True, chunks=2).compute() + gpu=True, chunks=2).compute() desc_vals = r_desc.data.get() if hasattr(r_desc.data, 'get') \ else r_desc.values asc_vals = r_asc.data.get() if hasattr(r_asc.data, 'get') \ diff --git a/xrspatial/tests/test_rasterize_all_touched_supercover_2169.py b/xrspatial/tests/test_rasterize_all_touched_supercover_2169.py index 41fd8bdba..c2d08bc39 100644 --- a/xrspatial/tests/test_rasterize_all_touched_supercover_2169.py +++ b/xrspatial/tests/test_rasterize_all_touched_supercover_2169.py @@ -76,14 +76,14 @@ def _rio_mask(poly): ) -def _xs_mask(poly, *, chunks=None, use_cuda=False): +def _xs_mask(poly, *, chunks=None, gpu=False): """xrspatial mask for ``poly`` on the requested backend.""" kwargs = dict(width=_W, height=_H, bounds=_BOUNDS, all_touched=True, fill=0) if chunks is not None: kwargs['chunks'] = chunks - if use_cuda: - kwargs['use_cuda'] = True + if gpu: + kwargs['gpu'] = True arr = rasterize([(poly, 1.0)], **kwargs) values = arr.data # dask -> concrete; cupy -> host numpy. @@ -175,7 +175,7 @@ class TestCupySupercoverParity: @pytest.mark.parametrize("name,poly", list(_cases().items())) def test_matches_rasterio(self, name, poly): rio = _rio_mask(poly) - xs = _xs_mask(poly, use_cuda=True) + xs = _xs_mask(poly, gpu=True) assert np.array_equal(rio, xs), ( f"{name}: rio={int(rio.sum())} xs={int(xs.sum())}" ) @@ -191,7 +191,7 @@ class TestDaskCupySupercoverParity: @pytest.mark.parametrize("name,poly", list(_cases().items())) def test_matches_rasterio(self, name, poly): rio = _rio_mask(poly) - xs = _xs_mask(poly, chunks=(_H // 2, _W // 2), use_cuda=True) + xs = _xs_mask(poly, chunks=(_H // 2, _W // 2), gpu=True) assert np.array_equal(rio, xs), ( f"{name}: rio={int(rio.sum())} xs={int(xs.sum())}" ) diff --git a/xrspatial/tests/test_rasterize_coverage_2026_05_17.py b/xrspatial/tests/test_rasterize_coverage_2026_05_17.py index a889d00b0..df15574ee 100644 --- a/xrspatial/tests/test_rasterize_coverage_2026_05_17.py +++ b/xrspatial/tests/test_rasterize_coverage_2026_05_17.py @@ -137,7 +137,7 @@ def test_polygon_eager_cupy_matches_numpy(self): width=1, height=1, bounds=(0, 0, 5, 5)) cp_r = rasterize([(box(0, 0, 5, 5), 7.0)], width=1, height=1, bounds=(0, 0, 5, 5), - use_cuda=True) + gpu=True) assert cp_r.shape == (1, 1) # Pin the absolute value too: a co-regression in eager numpy and # eager cupy (both writing fill instead of the burn value) would @@ -166,7 +166,7 @@ def test_polygon_dask_cupy_matches_numpy(self): width=1, height=1, bounds=(0, 0, 5, 5)) dkcp_r = rasterize([(box(0, 0, 5, 5), 7.0)], width=1, height=1, bounds=(0, 0, 5, 5), - chunks=(1, 1), use_cuda=True) + chunks=(1, 1), gpu=True) assert dkcp_r.shape == (1, 1) assert _as_numpy(dkcp_r)[0, 0] == 7.0 np.testing.assert_array_equal(np_r.values, _as_numpy(dkcp_r)) @@ -251,11 +251,11 @@ def test_like_width_height_override(self): assert r.dtype == np.float32 @skip_no_cuda - def test_like_with_use_cuda(self): + def test_like_with_gpu(self): """``like=`` works on the cupy backend (dtype + shape inherited).""" template = self._template(dtype=np.float32) r = rasterize([(box(0, 0, 6, 4), 9.0)], - like=template, fill=0, use_cuda=True) + like=template, fill=0, gpu=True) assert r.shape == template.shape assert r.dtype == np.float32 assert isinstance(r.data, cupy.ndarray) @@ -278,7 +278,7 @@ def test_like_with_dask_cupy(self): template = self._template(dtype=np.float32) r = rasterize([(box(0, 0, 6, 4), 9.0)], like=template, fill=0, chunks=(2, 3), - use_cuda=True) + gpu=True) assert r.shape == template.shape assert r.dtype == np.float32 @@ -366,7 +366,7 @@ def test_scalar_resolution_cupy_matches_numpy(self): resolution=1.0, bounds=(0, 0, 5, 5), fill=0) cp_r = rasterize([(box(0, 0, 5, 5), 1.0)], resolution=1.0, bounds=(0, 0, 5, 5), fill=0, - use_cuda=True) + gpu=True) assert cp_r.shape == (5, 5) # Positive pin: polygon covers the full 5x5 grid. assert int((_as_numpy(cp_r) == 1.0).sum()) == 25 @@ -392,7 +392,7 @@ def test_scalar_resolution_dask_cupy_matches_numpy(self): resolution=1.0, bounds=(0, 0, 5, 5), fill=0) dkcp_r = rasterize([(box(0, 0, 5, 5), 1.0)], resolution=1.0, bounds=(0, 0, 5, 5), fill=0, - chunks=(2, 2), use_cuda=True) + chunks=(2, 2), gpu=True) assert dkcp_r.shape == (5, 5) assert int((_as_numpy(dkcp_r) == 1.0).sum()) == 25 np.testing.assert_array_equal(np_r.values, _as_numpy(dkcp_r)) @@ -469,7 +469,7 @@ def test_collection_eager_cupy_matches_numpy(self): np_r = rasterize([(gc, 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), fill=0) cp_r = rasterize([(gc, 1.0)], width=10, height=10, - bounds=(0, 0, 10, 10), fill=0, use_cuda=True) + bounds=(0, 0, 10, 10), fill=0, gpu=True) # 25 polygon cells + 1 point cell = 26 (eager case pins this too). assert int((_as_numpy(cp_r) == 1.0).sum()) == 26 np.testing.assert_array_equal(np_r.values, _as_numpy(cp_r)) @@ -494,7 +494,7 @@ def test_collection_dask_cupy_matches_numpy(self): bounds=(0, 0, 10, 10), fill=0) dkcp_r = rasterize([(gc, 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), fill=0, - chunks=(5, 5), use_cuda=True) + chunks=(5, 5), gpu=True) assert int((_as_numpy(dkcp_r) == 1.0).sum()) == 26 np.testing.assert_array_equal(np_r.values, _as_numpy(dkcp_r)) @@ -526,7 +526,7 @@ def test_eager_cupy_all_touched_matches_numpy(self): all_touched=True) cp_r = rasterize([(geom, 1.0)], width=5, height=5, bounds=(0, 0, 5, 5), fill=0, - all_touched=True, use_cuda=True) + all_touched=True, gpu=True) np.testing.assert_array_equal(np_r.values, _as_numpy(cp_r)) # Sanity: the touched mode lights the four corner cells. assert int((np_r.values == 1.0).sum()) == 4 @@ -540,10 +540,10 @@ def test_eager_cupy_all_touched_superset_of_default(self): geom = box(1.9, 1.9, 2.1, 2.1) cp_default = rasterize([(geom, 1.0)], width=5, height=5, bounds=(0, 0, 5, 5), fill=0, - use_cuda=True) + gpu=True) cp_touched = rasterize([(geom, 1.0)], width=5, height=5, bounds=(0, 0, 5, 5), fill=0, - all_touched=True, use_cuda=True) + all_touched=True, gpu=True) default_mask = (_as_numpy(cp_default) == 1.0) touched_mask = (_as_numpy(cp_touched) == 1.0) # all_touched must fill everywhere the default mode filled. diff --git a/xrspatial/tests/test_rasterize_coverage_2026_05_21.py b/xrspatial/tests/test_rasterize_coverage_2026_05_21.py index df063d9dd..bcd83c214 100644 --- a/xrspatial/tests/test_rasterize_coverage_2026_05_21.py +++ b/xrspatial/tests/test_rasterize_coverage_2026_05_21.py @@ -103,9 +103,9 @@ def _materialise(result): _BACKEND_KWARGS = { 'numpy': {}, - 'cupy': {'use_cuda': True}, + 'cupy': {'gpu': True}, 'dask_numpy': {'chunks': (5, 5)}, - 'dask_cupy': {'use_cuda': True, 'chunks': (5, 5)}, + 'dask_cupy': {'gpu': True, 'chunks': (5, 5)}, } @@ -404,7 +404,7 @@ def test_multi_column_sum_cupy_matches_numpy(self): fill=0) cp_r = rasterize(gdf, columns=['num', 'den'], merge='sum', width=10, height=5, bounds=(0, 0, 10, 5), - fill=0, use_cuda=True) + fill=0, gpu=True) np.testing.assert_array_equal(np_r.values, _materialise(cp_r)) @skip_no_cuda @@ -416,7 +416,7 @@ def test_multi_column_sum_dask_cupy_matches_numpy(self): fill=0) dc_r = rasterize(gdf, columns=['num', 'den'], merge='sum', width=10, height=5, bounds=(0, 0, 10, 5), - fill=0, use_cuda=True, chunks=(3, 3)) + fill=0, gpu=True, chunks=(3, 3)) np.testing.assert_array_equal(np_r.values, _materialise(dc_r)) @skip_no_cuda @@ -428,7 +428,7 @@ def test_multi_column_props_count_cupy(self): gdf = self._fixture() cp_r = rasterize(gdf, columns=['num', 'den'], merge='count', width=10, height=5, bounds=(0, 0, 10, 5), - fill=0, use_cuda=True) + fill=0, gpu=True) data = _materialise(cp_r) # Every covered pixel has count==1; uncovered pixels are 0. assert (data == 1).sum() == 50 # full 10x5 covered by union @@ -449,7 +449,7 @@ def test_multi_column_three_columns_cupy(self): fill=0) cp_r = rasterize(gdf, columns=['a', 'b', 'c'], merge='sum', width=10, height=5, bounds=(0, 0, 10, 5), - fill=0, use_cuda=True) + fill=0, gpu=True) np.testing.assert_array_equal(np_r.values, _materialise(cp_r)) diff --git a/xrspatial/tests/test_rasterize_coverage_2026_05_27.py b/xrspatial/tests/test_rasterize_coverage_2026_05_27.py index 2a440b486..c12bb1d60 100644 --- a/xrspatial/tests/test_rasterize_coverage_2026_05_27.py +++ b/xrspatial/tests/test_rasterize_coverage_2026_05_27.py @@ -3,7 +3,7 @@ Closes documented public-API gaps left after the pass-1 (2026-05-17) and pass-2 (2026-05-21) audits: -- Cat 1 HIGH -- The eager cupy (``use_cuda=True``, no ``chunks=``) backend +- Cat 1 HIGH -- The eager cupy (``gpu=True``, no ``chunks=``) backend has no parametrised merge-mode parity test. The dask+numpy and dask+cupy backends pin all six built-in merge modes (``last`` / ``first`` / ``max`` / ``min`` / ``sum`` / ``count``) against eager numpy via @@ -31,7 +31,7 @@ ``_run_cupy`` with zero geometries (so the bbox/edge/segment buffers are zero-sized cupy arrays) has no direct test, and a regression short-circuiting to numpy on the empty branch would silently break the - "use_cuda=True returns cupy.ndarray" contract. + "gpu=True returns cupy.ndarray" contract. - Cat 2 MEDIUM -- All-equal property values on the count merge mode. ``count`` ignores the burned value and counts overlapping geometries. @@ -155,7 +155,7 @@ def test_merge_mode_eager_cupy_matches_numpy(self, merge_mode): cp_result = rasterize( _EAGER_CUPY_MERGE_PAIRS, width=10, height=10, bounds=(0, 0, 10, 10), fill=0, merge=merge_mode, - use_cuda=True, + gpu=True, ) # ``count`` returns an integer-valued float, the rest are the # burned property values; both compare bit-exact with @@ -234,7 +234,7 @@ def test_overlapping_points_merge_mode(self, merge_mode): cp_result = rasterize( pairs, width=5, height=5, bounds=(0, 0, 5, 5), fill=0, merge=merge_mode, - use_cuda=True, + gpu=True, ) np.testing.assert_array_equal( np_result.values, _materialise(cp_result)) @@ -255,17 +255,17 @@ class TestEagerCupyEmptyGeometryList: eager cupy path (no ``chunks=``) feeds zero-sized cupy arrays into ``_gpu_init_buffers`` and ``_gpu_finalize_buffers`` and a regression short-circuiting to numpy on the empty-input branch would silently - break the "use_cuda=True returns a cupy.ndarray" contract. + break the "gpu=True returns a cupy.ndarray" contract. """ def test_empty_list_returns_cupy_filled_with_fill(self): result = rasterize( [], width=8, height=8, - bounds=(0, 0, 8, 8), fill=-1.0, use_cuda=True, + bounds=(0, 0, 8, 8), fill=-1.0, gpu=True, ) - # Backend contract: use_cuda=True must return a cupy.ndarray. + # Backend contract: gpu=True must return a cupy.ndarray. assert isinstance(result.data, cupy.ndarray), \ - "use_cuda=True with empty input must keep cupy backend" + "gpu=True with empty input must keep cupy backend" host = cupy.asnumpy(result.data) assert host.shape == (8, 8) assert np.all(host == -1.0) @@ -274,7 +274,7 @@ def test_empty_list_default_nan_fill_returns_cupy(self): """Default NaN fill must also stay on the GPU.""" result = rasterize( [], width=5, height=5, - bounds=(0, 0, 5, 5), use_cuda=True, + bounds=(0, 0, 5, 5), gpu=True, ) assert isinstance(result.data, cupy.ndarray) host = cupy.asnumpy(result.data) @@ -345,7 +345,7 @@ def test_count_eager_cupy_matches_numpy(self): cp_result = rasterize( _ALL_EQUAL_PAIRS, width=10, height=10, bounds=(0, 0, 10, 10), fill=0, merge='count', - use_cuda=True, + gpu=True, ) np.testing.assert_array_equal( np_result.values, _materialise(cp_result)) @@ -374,7 +374,7 @@ def test_count_dask_cupy_matches_numpy(self): dc_result = rasterize( _ALL_EQUAL_PAIRS, width=10, height=10, bounds=(0, 0, 10, 10), fill=0, merge='count', - use_cuda=True, chunks=(5, 5), + gpu=True, chunks=(5, 5), ) np.testing.assert_array_equal( np_result.values, _materialise(dc_result)) @@ -414,7 +414,7 @@ def test_name_propagated_dask_numpy(self): def test_name_propagated_eager_cupy(self): result = rasterize( self._simple_pair(), width=5, height=5, - bounds=(0, 0, 5, 5), name='burned_cupy', use_cuda=True, + bounds=(0, 0, 5, 5), name='burned_cupy', gpu=True, ) assert result.name == 'burned_cupy' @@ -424,6 +424,6 @@ def test_name_propagated_dask_cupy(self): result = rasterize( self._simple_pair(), width=5, height=5, bounds=(0, 0, 5, 5), name='burned_dask_cupy', - use_cuda=True, chunks=(3, 3), + gpu=True, chunks=(3, 3), ) assert result.name == 'burned_dask_cupy' diff --git a/xrspatial/tests/test_rasterize_crs_mismatch_3058.py b/xrspatial/tests/test_rasterize_crs_mismatch_3058.py index e4ddb330d..b93fcecfb 100644 --- a/xrspatial/tests/test_rasterize_crs_mismatch_3058.py +++ b/xrspatial/tests/test_rasterize_crs_mismatch_3058.py @@ -228,7 +228,7 @@ def test_mismatch_raises_cupy(self): _gdf(crs='EPSG:4326'), like=_make_like(crs_attr='EPSG:3857'), column='value', - use_cuda=True, + gpu=True, ) @pytest.mark.skipif(not has_cuda, reason="CUDA / CuPy not available") @@ -237,6 +237,6 @@ def test_match_ok_cupy(self): _gdf(crs='EPSG:4326'), like=_make_like(crs_attr='EPSG:4326'), column='value', - use_cuda=True, + gpu=True, ) assert result.attrs.get('crs') == 'EPSG:4326' diff --git a/xrspatial/tests/test_rasterize_descending_x_2568.py b/xrspatial/tests/test_rasterize_descending_x_2568.py index 4102f98cc..96decb1b5 100644 --- a/xrspatial/tests/test_rasterize_descending_x_2568.py +++ b/xrspatial/tests/test_rasterize_descending_x_2568.py @@ -175,10 +175,10 @@ def test_dask_numpy_descending_x_matches_ascending(self): def test_cupy_descending_x_matches_ascending(self): geom = [(box(0, 0, 1, 1), 1.0)] r_asc = rasterize( - geom, like=_like_2568(x_descending=False), fill=0, use_cuda=True, + geom, like=_like_2568(x_descending=False), fill=0, gpu=True, ) r_desc = rasterize( - geom, like=_like_2568(x_descending=True), fill=0, use_cuda=True, + geom, like=_like_2568(x_descending=True), fill=0, gpu=True, ) asc_vals = r_asc.data.get() if hasattr(r_asc.data, 'get') \ else r_asc.values @@ -197,11 +197,11 @@ def test_dask_cupy_descending_x_matches_ascending(self): geom = [(box(0, 0, 1, 1), 1.0)] r_asc = rasterize( geom, like=_like_2568(x_descending=False), fill=0, - use_cuda=True, chunks=2, + gpu=True, chunks=2, ).compute() r_desc = rasterize( geom, like=_like_2568(x_descending=True), fill=0, - use_cuda=True, chunks=2, + gpu=True, chunks=2, ).compute() asc_vals = r_asc.data.get() if hasattr(r_asc.data, 'get') \ else r_asc.values diff --git a/xrspatial/tests/test_rasterize_fill_dtype_3054.py b/xrspatial/tests/test_rasterize_fill_dtype_3054.py index 03168c7d2..72996ec7a 100644 --- a/xrspatial/tests/test_rasterize_fill_dtype_3054.py +++ b/xrspatial/tests/test_rasterize_fill_dtype_3054.py @@ -105,7 +105,7 @@ def test_out_of_range_int_fill_raises_dask_numpy(): def test_out_of_range_int_fill_raises_cupy(): with pytest.raises(ValueError, match="cannot be represented"): rasterize(_square(), width=10, height=10, bounds=(0, 0, 10, 10), - fill=-9999, dtype=np.uint8, use_cuda=True) + fill=-9999, dtype=np.uint8, gpu=True) @skip_no_shapely @@ -114,7 +114,7 @@ def test_out_of_range_int_fill_raises_cupy(): def test_out_of_range_int_fill_raises_dask_cupy(): with pytest.raises(ValueError, match="cannot be represented"): rasterize(_square(), width=10, height=10, bounds=(0, 0, 10, 10), - fill=-9999, dtype=np.uint8, use_cuda=True, chunks=5) + fill=-9999, dtype=np.uint8, gpu=True, chunks=5) @skip_no_shapely @@ -154,7 +154,7 @@ def test_bool_dtype_nan_fill_raises_dask_numpy(): def test_bool_dtype_nan_fill_raises_cupy(): with pytest.raises(ValueError, match="cannot be represented"): rasterize(_square(), width=10, height=10, bounds=(0, 0, 10, 10), - fill=np.nan, dtype=np.bool_, use_cuda=True) + fill=np.nan, dtype=np.bool_, gpu=True) @skip_no_shapely @@ -163,7 +163,7 @@ def test_bool_dtype_nan_fill_raises_cupy(): def test_bool_dtype_nan_fill_raises_dask_cupy(): with pytest.raises(ValueError, match="cannot be represented"): rasterize(_square(), width=10, height=10, bounds=(0, 0, 10, 10), - fill=np.nan, dtype=np.bool_, use_cuda=True, chunks=5) + fill=np.nan, dtype=np.bool_, gpu=True, chunks=5) @skip_no_shapely diff --git a/xrspatial/tests/test_rasterize_gpu_alias_3089.py b/xrspatial/tests/test_rasterize_gpu_alias_3089.py new file mode 100644 index 000000000..297592bb4 --- /dev/null +++ b/xrspatial/tests/test_rasterize_gpu_alias_3089.py @@ -0,0 +1,136 @@ +"""Issue #3089: rasterize() renamed its GPU opt-in from ``use_cuda`` to +``gpu`` to match ``open_geotiff(gpu=True)``. + +``use_cuda`` stays as a deprecated keyword alias: it still selects the GPU +backend but emits a ``DeprecationWarning``, and combining it with +``gpu=True`` raises ``TypeError``. These tests pin the shim and the +positional-compatibility guarantee (``gpu`` occupies the slot ``use_cuda`` +used to, so positional callers are unaffected). +""" +import inspect +import warnings + +import numpy as np +import pytest + +try: + from shapely.geometry import box + has_shapely = True +except ImportError: + has_shapely = False + +if has_shapely: + from xrspatial.rasterize import rasterize + +try: + import cupy + from numba import cuda + has_gpu = cuda.is_available() +except ImportError: + cupy = None + has_gpu = False + +pytestmark = pytest.mark.skipif( + not has_shapely, reason="shapely not installed" +) + +requires_gpu = pytest.mark.skipif( + not has_gpu, reason="CUDA GPU not available") + + +def _pairs(): + return [(box(0, 0, 5, 5), 1.0), (box(3, 3, 9, 9), 2.0)] + + +def _kw(): + return dict(width=10, height=10, bounds=(0, 0, 10, 10), + merge='sum', fill=0) + + +def _to_numpy(result): + data = result.data + if hasattr(data, 'get'): + return data.get() + return np.asarray(data) + + +def test_gpu_is_tenth_positional_param(): + """``gpu`` must sit exactly where ``use_cuda`` used to, so existing + positional callers keep selecting the GPU backend.""" + params = list(inspect.signature(rasterize).parameters) + assert params[9] == 'gpu' + # the deprecated alias is appended last so it shifts nothing + assert params[-1] == 'use_cuda' + + +def test_default_emits_no_deprecation_warning(): + with warnings.catch_warnings(): + warnings.simplefilter('error', DeprecationWarning) + rasterize(_pairs(), **_kw()) + + +def test_use_cuda_false_warns_and_runs_cpu(): + with pytest.warns(DeprecationWarning, match='use gpu='): + result = rasterize(_pairs(), use_cuda=False, **_kw()) + assert isinstance(result.data, np.ndarray) + + +def test_gpu_true_and_use_cuda_raises(): + with pytest.raises(TypeError, match="deprecated alias"): + rasterize(_pairs(), gpu=True, use_cuda=True, **_kw()) + with pytest.raises(TypeError, match="deprecated alias"): + rasterize(_pairs(), gpu=True, use_cuda=False, **_kw()) + + +@requires_gpu +def test_gpu_true_matches_numpy(): + expected = rasterize(_pairs(), **_kw()) + result = rasterize(_pairs(), gpu=True, **_kw()) + assert isinstance(result.data, cupy.ndarray) + np.testing.assert_array_equal(_to_numpy(result), expected.data) + + +@requires_gpu +def test_use_cuda_true_warns_and_matches_gpu_true(): + expected = rasterize(_pairs(), gpu=True, **_kw()) + with pytest.warns(DeprecationWarning, match='use gpu='): + result = rasterize(_pairs(), use_cuda=True, **_kw()) + assert isinstance(result.data, cupy.ndarray) + np.testing.assert_array_equal(_to_numpy(result), _to_numpy(expected)) + + +@requires_gpu +def test_dask_cupy_via_gpu_and_alias(): + import dask.array as da + + lazy = rasterize(_pairs(), gpu=True, chunks=5, **_kw()) + assert isinstance(lazy.data, da.Array) + assert isinstance(lazy.data._meta, cupy.ndarray) + + with pytest.warns(DeprecationWarning, match='use gpu='): + lazy_alias = rasterize(_pairs(), use_cuda=True, chunks=5, **_kw()) + assert isinstance(lazy_alias.data._meta, cupy.ndarray) + + np.testing.assert_array_equal( + _to_numpy(lazy.compute()), _to_numpy(lazy_alias.compute())) + + +@requires_gpu +def test_clip_polygon_dask_cupy_emits_no_deprecation_warning(): + """polygon_clip's internal rasterize call must use the new name.""" + import dask.array as da + import xarray as xr + + from xrspatial.polygon_clip import clip_polygon + + data = cupy.ones((10, 10), dtype=cupy.float64) + raster = xr.DataArray( + da.from_array(data, chunks=(5, 5)), + dims=['y', 'x'], + coords={'y': np.linspace(9.5, 0.5, 10), + 'x': np.linspace(0.5, 9.5, 10)}, + ) + with warnings.catch_warnings(): + warnings.simplefilter('error', DeprecationWarning) + clipped = clip_polygon(raster, box(2, 2, 8, 8)) + assert clipped is not None diff --git a/xrspatial/tests/test_rasterize_gpu_callable_warn_3057.py b/xrspatial/tests/test_rasterize_gpu_callable_warn_3057.py index 6b9eea17f..fc93cb02c 100644 --- a/xrspatial/tests/test_rasterize_gpu_callable_warn_3057.py +++ b/xrspatial/tests/test_rasterize_gpu_callable_warn_3057.py @@ -2,7 +2,7 @@ non-atomic read-modify-write, so overlap pixels are nondeterministic. ``rasterize`` must warn the caller when a callable ``merge`` is paired -with ``use_cuda=True``. The warning fires after the CuPy import check +with ``gpu=True``. The warning fires after the CuPy import check but before the GPU kernel launch, so these tests need CuPy importable but not an actual GPU device: they record warnings manually and ignore the numba/CUDA error the (device-less) launch raises afterwards. @@ -71,16 +71,16 @@ def _overlap_warnings(**kwargs): @skip_no_cupy def test_callable_gpu_merge_warns(): - """Callable merge + use_cuda=True emits the overlap UserWarning.""" - matched = _overlap_warnings(merge=_my_sum, use_cuda=True) + """Callable merge + gpu=True emits the overlap UserWarning.""" + matched = _overlap_warnings(merge=_my_sum, gpu=True) assert len(matched) == 1 assert matched[0].category is UserWarning @skip_no_cupy def test_callable_gpu_merge_chunks_warns(): - """The warning also fires for the dask+cupy path (chunks + use_cuda).""" - matched = _overlap_warnings(merge=_my_sum, use_cuda=True, chunks=(5, 5)) + """The warning also fires for the dask+cupy path (chunks + gpu).""" + matched = _overlap_warnings(merge=_my_sum, gpu=True, chunks=(5, 5)) assert len(matched) == 1 assert matched[0].category is UserWarning @@ -97,4 +97,4 @@ def test_callable_cpu_merge_does_not_warn(): def test_builtin_gpu_merge_does_not_warn(): """A built-in string merge on the GPU backend stays silent -- it uses atomics and is deterministic over overlap.""" - assert _overlap_warnings(merge='sum', use_cuda=True) == [] + assert _overlap_warnings(merge='sum', gpu=True) == [] diff --git a/xrspatial/tests/test_rasterize_gpu_race_2167.py b/xrspatial/tests/test_rasterize_gpu_race_2167.py index f1f6e9557..e3586d4cc 100644 --- a/xrspatial/tests/test_rasterize_gpu_race_2167.py +++ b/xrspatial/tests/test_rasterize_gpu_race_2167.py @@ -59,10 +59,10 @@ def _as_numpy(arr): return np.asarray(data) -def _run(geom_list, merge, use_cuda, all_touched=False): +def _run(geom_list, merge, gpu, all_touched=False): return rasterize( geom_list, width=WIDTH, height=HEIGHT, bounds=BOUNDS, - merge=merge, use_cuda=use_cuda, fill=np.nan, + merge=merge, gpu=gpu, fill=np.nan, all_touched=all_touched, ) @@ -117,8 +117,8 @@ def _duplicate_segments(): @pytest.mark.parametrize('merge', MERGES) def test_cupy_matches_numpy_on_overlap(scenario, merge): geoms = SCENARIOS[scenario]() - expected = _as_numpy(_run(geoms, merge, use_cuda=False)) - actual = _as_numpy(_run(geoms, merge, use_cuda=True)) + expected = _as_numpy(_run(geoms, merge, gpu=False)) + actual = _as_numpy(_run(geoms, merge, gpu=True)) # Pixels touched by no geometry should be ``fill`` (NaN here) on # both backends. np.testing.assert_array_equal treats NaN==NaN @@ -142,11 +142,11 @@ def test_cupy_matches_numpy_on_overlap(scenario, merge): @pytest.mark.parametrize('merge', MERGES) def test_cupy_is_deterministic_across_runs(scenario, merge): geoms = SCENARIOS[scenario]() - first = _as_numpy(_run(geoms, merge, use_cuda=True)) + first = _as_numpy(_run(geoms, merge, gpu=True)) # Six repeats is enough to surface a thread-interleaving race # without making the test slow. for _ in range(5): - again = _as_numpy(_run(geoms, merge, use_cuda=True)) + again = _as_numpy(_run(geoms, merge, gpu=True)) np.testing.assert_allclose( again, first, rtol=0, atol=0, equal_nan=True, err_msg=( @@ -166,7 +166,7 @@ def test_cupy_is_deterministic_across_runs(scenario, merge): def test_sum_of_coincident_points_equals_total(): geoms = _coincident_points() # The first three points all land in the same pixel; sum should be 7. - result = _as_numpy(_run(geoms, 'sum', use_cuda=True)) + result = _as_numpy(_run(geoms, 'sum', gpu=True)) # Find the pixel with the largest accumulated value. finite = result[np.isfinite(result)] assert finite.size > 0 @@ -178,7 +178,7 @@ def test_sum_of_coincident_points_equals_total(): def test_count_of_coincident_points_equals_three(): geoms = _coincident_points() - result = _as_numpy(_run(geoms, 'count', use_cuda=True)) + result = _as_numpy(_run(geoms, 'count', gpu=True)) finite = result[np.isfinite(result)] assert 3.0 in finite, ( f"expected the three-point pixel to count 3; got {finite}" @@ -203,8 +203,8 @@ def _shared_boundary_polygons(): @pytest.mark.parametrize('merge', MERGES) def test_cupy_matches_numpy_all_touched_shared_boundary(merge): geoms = _shared_boundary_polygons() - expected = _as_numpy(_run(geoms, merge, use_cuda=False, all_touched=True)) - actual = _as_numpy(_run(geoms, merge, use_cuda=True, all_touched=True)) + expected = _as_numpy(_run(geoms, merge, gpu=False, all_touched=True)) + actual = _as_numpy(_run(geoms, merge, gpu=True, all_touched=True)) np.testing.assert_allclose( actual, expected, rtol=0, atol=0, equal_nan=True, err_msg=( @@ -218,9 +218,9 @@ def test_cupy_matches_numpy_all_touched_shared_boundary(merge): @pytest.mark.parametrize('merge', MERGES) def test_cupy_deterministic_all_touched_shared_boundary(merge): geoms = _shared_boundary_polygons() - first = _as_numpy(_run(geoms, merge, use_cuda=True, all_touched=True)) + first = _as_numpy(_run(geoms, merge, gpu=True, all_touched=True)) for _ in range(5): - again = _as_numpy(_run(geoms, merge, use_cuda=True, all_touched=True)) + again = _as_numpy(_run(geoms, merge, gpu=True, all_touched=True)) np.testing.assert_allclose( again, first, rtol=0, atol=0, equal_nan=True, err_msg=( diff --git a/xrspatial/tests/test_rasterize_int_precision_3056.py b/xrspatial/tests/test_rasterize_int_precision_3056.py index a67636ffa..453dae326 100644 --- a/xrspatial/tests/test_rasterize_int_precision_3056.py +++ b/xrspatial/tests/test_rasterize_int_precision_3056.py @@ -200,7 +200,7 @@ def test_unsafe_int_raises_cupy(): with pytest.raises(ValueError, match=_MATCH): rasterize([(box(0, 0, 5, 5), UNSAFE)], width=4, height=4, bounds=(0, 0, 5, 5), - fill=0, dtype=np.int64, use_cuda=True) + fill=0, dtype=np.int64, gpu=True) @skip_no_shapely @@ -211,4 +211,4 @@ def test_unsafe_int_raises_dask_cupy(): with pytest.raises(ValueError, match=_MATCH): rasterize([(box(0, 0, 5, 5), UNSAFE)], width=4, height=4, bounds=(0, 0, 5, 5), - fill=0, dtype=np.int64, use_cuda=True, chunks=2) + fill=0, dtype=np.int64, gpu=True, chunks=2) diff --git a/xrspatial/tests/test_rasterize_linearring_3055.py b/xrspatial/tests/test_rasterize_linearring_3055.py index bd3666325..6d6efbceb 100644 --- a/xrspatial/tests/test_rasterize_linearring_3055.py +++ b/xrspatial/tests/test_rasterize_linearring_3055.py @@ -182,7 +182,7 @@ def test_dask_burns_ring(self): @skip_no_cuda def test_cupy_burns_ring(self): result = rasterize([(_RING, 1.0)], width=5, height=5, - bounds=(0, 0, 5, 5), fill=0, use_cuda=True) + bounds=(0, 0, 5, 5), fill=0, gpu=True) np.testing.assert_array_equal(result.data.get(), _EXPECTED) @skip_no_cuda @@ -190,7 +190,7 @@ def test_cupy_burns_ring(self): def test_dask_cupy_burns_ring(self): result = rasterize([(_RING, 1.0)], width=5, height=5, bounds=(0, 0, 5, 5), fill=0, - use_cuda=True, chunks=3) + gpu=True, chunks=3) np.testing.assert_array_equal( np.asarray(result.data.map_blocks(lambda b: b.get())), _EXPECTED) diff --git a/xrspatial/tests/test_rasterize_nan_int_fill_2504.py b/xrspatial/tests/test_rasterize_nan_int_fill_2504.py index 1adff3d13..cfb1bd18d 100644 --- a/xrspatial/tests/test_rasterize_nan_int_fill_2504.py +++ b/xrspatial/tests/test_rasterize_nan_int_fill_2504.py @@ -95,7 +95,7 @@ def test_int_dtype_default_nan_fill_raises_cupy(): with pytest.raises(ValueError, match="cannot be represented"): rasterize([(box(2, 2, 8, 8), 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), - dtype=np.int32, use_cuda=True) + dtype=np.int32, gpu=True) @skip_no_shapely @@ -106,7 +106,7 @@ def test_int_dtype_default_nan_fill_raises_dask_cupy(): with pytest.raises(ValueError, match="cannot be represented"): rasterize([(box(2, 2, 8, 8), 1.0)], width=10, height=10, bounds=(0, 0, 10, 10), - dtype=np.int32, use_cuda=True, chunks=5) + dtype=np.int32, gpu=True, chunks=5) @skip_no_shapely diff --git a/xrspatial/tests/test_rasterize_nan_propagation_2255.py b/xrspatial/tests/test_rasterize_nan_propagation_2255.py index 0886dd1dd..accda44b8 100644 --- a/xrspatial/tests/test_rasterize_nan_propagation_2255.py +++ b/xrspatial/tests/test_rasterize_nan_propagation_2255.py @@ -67,11 +67,11 @@ def _materialise(result): ALL_BACKENDS = [ pytest.param('numpy', {}, id='numpy'), - pytest.param('cupy', {'use_cuda': True}, + pytest.param('cupy', {'gpu': True}, marks=skip_no_cuda, id='cupy'), pytest.param('dask_numpy', {'chunks': (5, 5)}, marks=skip_no_dask, id='dask_numpy'), - pytest.param('dask_cupy', {'use_cuda': True, 'chunks': (5, 5)}, + pytest.param('dask_cupy', {'gpu': True, 'chunks': (5, 5)}, marks=[skip_no_cuda, skip_no_dask], id='dask_cupy'), ] diff --git a/xrspatial/tests/test_rasterize_props_hoist_2506.py b/xrspatial/tests/test_rasterize_props_hoist_2506.py index eba744e16..3acfc32ef 100644 --- a/xrspatial/tests/test_rasterize_props_hoist_2506.py +++ b/xrspatial/tests/test_rasterize_props_hoist_2506.py @@ -169,7 +169,7 @@ def test_cupy_all_touched_matches_numpy(self, merge): got = rasterize(geoms, width=24, height=24, bounds=(0, 0, 10, 10), all_touched=True, merge=merge, fill=0.0, - use_cuda=True) + gpu=True) np.testing.assert_allclose( self._to_numpy(got), self._to_numpy(ref), atol=0, rtol=0, err_msg=f"cupy all_touched != numpy for merge={merge!r}", @@ -190,7 +190,7 @@ def test_dask_cupy_all_touched_runs(self, merge): got = rasterize(geoms, width=24, height=24, bounds=(0, 0, 10, 10), all_touched=True, merge=merge, fill=0.0, - use_cuda=True, chunks=8) + gpu=True, chunks=8) out = self._to_numpy(got) assert out.shape == (24, 24) # Some interior pixels must have been burned by at least one diff --git a/xrspatial/tests/test_rasterize_resolution_exact_2573.py b/xrspatial/tests/test_rasterize_resolution_exact_2573.py index c51c9f341..f6efda02e 100644 --- a/xrspatial/tests/test_rasterize_resolution_exact_2573.py +++ b/xrspatial/tests/test_rasterize_resolution_exact_2573.py @@ -245,7 +245,7 @@ def test_cupy_matches_numpy(self): resolution=0.3, bounds=(0, 0, 1, 1), fill=0) cp_r = rasterize([(box(0, 0, 1, 1), 1.0)], resolution=0.3, bounds=(0, 0, 1, 1), fill=0, - use_cuda=True) + gpu=True) assert cp_r.shape == np_r.shape == (4, 4) assert _cell_size(cp_r.x) == pytest.approx(0.3, abs=1e-9) cp_vals = cupy.asnumpy(cp_r.data) @@ -269,7 +269,7 @@ def test_dask_cupy_matches_numpy(self): resolution=0.3, bounds=(0, 0, 1, 1), fill=0) dkcp_r = rasterize([(box(0, 0, 1, 1), 1.0)], resolution=0.3, bounds=(0, 0, 1, 1), fill=0, - chunks=(2, 2), use_cuda=True) + chunks=(2, 2), gpu=True) assert dkcp_r.shape == np_r.shape == (4, 4) assert _cell_size(dkcp_r.x) == pytest.approx(0.3, abs=1e-9) np.testing.assert_array_equal(