Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/sweep-style-state.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module,last_inspected,issue,severity_max,categories_found,notes
geotiff,2026-05-27,2481,HIGH,1;3;4,"Bundled 387 flake8 + ~30 isort fixes since #2285/#2430. F401 x9, F811 x6, F841 x3. E501 x250 (mostly wrapped, 3 file-scope imports keep noqa: E402+E501). E252 x62, blank-line cluster, E128/E127 indents. importorskip imports use # noqa: E402. Cat 5 grep clean."
hydro-d8,2026-05-29,2705,HIGH,1;3;4,"flake8+isort over the 13 D8 files only (dinf/mfd out of scope). Cat 3 HIGH: F401 x2 (flow_length_d8 function-local _compute_accum_seeds never called; snap_pour_point_d8 module-level cuda_args unused) - both confirmed dead, no re-export. Cat 1: E127/E128 continuation-indent x90 (mostly multi-line def signatures); E302/E303 blank-line cluster in watershed_d8; E501 x4 (flow_path_d8 + snap_pour_point_d8, wrapped ternaries). Cat 4: isort import-block reordering on all 13 files. No Cat 2 (W-codes), no Cat 5 (grep clean: no bare except, mutable defaults, ==None/==True, or shadowed builtins). flake8+isort clean after fix; 385 D8 tests pass. flow_direction_d8 needed manual blank-line placement to satisfy both isort and E302."
polygonize,2026-05-27,2534,HIGH,1;3;4,"F401 line 58 (is_cupy_array unused, not re-exported). E127 lines 83/88 (overload continuation indent in generated_jit). isort: 5-line .utils import block collapses to one line at 100-char limit. Cat 2 clean. Cat 5 grep clean."
rasterize,2026-05-27,2503,HIGH,1;3,F401 line 15 + F811 line 1193 (paired: local import warnings shadowed unused module-level import); E306 line 1775 (nested @cuda.jit). isort clean. Cat 5 grep clean. Fix in PR #2507.
resample,2026-05-27,2543,MEDIUM,4,isort drift only: 4 multi-line parenthesised imports collapsed to single/one-per-line under line_length=100 (top-of-file scipy.ndimage + xrspatial.utils; local cupyx imports in _nan_aware_interp_cupy and _interp_block_cupy); two blank-line nits after import math in _run_dask_numpy/_run_dask_cupy. flake8 clean. Cat 5 grep clean. 169 resample tests pass.
Expand Down
16 changes: 5 additions & 11 deletions xrspatial/hydro/basin_d8.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,9 @@ class cupy: # type: ignore[no-redef]
except ImportError:
da = None

from xrspatial.utils import (
_validate_raster,
cuda_args,
has_cuda_and_cupy,
is_cupy_array,
is_dask_cupy,
ngjit,
)
from xrspatial.dataset_support import supports_dataset

from xrspatial.utils import (_validate_raster, cuda_args, has_cuda_and_cupy, is_cupy_array,
is_dask_cupy, ngjit)

# =====================================================================
# Memory guards
Expand Down Expand Up @@ -387,6 +380,7 @@ def _basins_make_pp_block(flow_dir_block, block_info=None):
def _basins_dask_cupy(flow_dir_da):
"""Dask+CuPy basins: native GPU via watershed infrastructure."""
import cupy as cp

from xrspatial.hydro.watershed_d8 import _watershed_dask_cupy

chunks_y = flow_dir_da.chunks[0]
Expand All @@ -404,7 +398,7 @@ def _basins_make_pp_block(flow_dir_block, block_info=None):
else np.asarray(flow_dir_block)
chunk_np = np.asarray(chunk_np, dtype=np.float64)
pp = _basins_init_labels(chunk_np, h, w, total_h, total_w,
row_off, col_off)
row_off, col_off)
return cp.asarray(np.where(pp >= 0, pp, np.nan))

pour_points_da = da.map_blocks(
Expand All @@ -420,7 +414,7 @@ def _basins_make_pp_block(flow_dir_block, block_info=None):

@supports_dataset
def basin_d8(flow_dir: xr.DataArray,
name: str = 'basin') -> xr.DataArray:
name: str = 'basin') -> xr.DataArray:
"""Delineate drainage basins: every cell labeled with its outlet ID.

Automatically identifies all outlets (pits and edge-exit cells)
Expand Down
37 changes: 15 additions & 22 deletions xrspatial/hydro/fill_d8.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,10 @@ class cupy: # type: ignore[no-redef]
except ImportError:
da = None

from xrspatial.utils import (
_validate_raster,
cuda_args,
has_cuda_and_cupy,
is_cupy_array,
is_dask_cupy,
ngjit,
)
from xrspatial.hydro._boundary_store import BoundaryStore
from xrspatial.dataset_support import supports_dataset

from xrspatial.hydro._boundary_store import BoundaryStore
from xrspatial.utils import (_validate_raster, cuda_args, has_cuda_and_cupy, is_cupy_array,
is_dask_cupy, ngjit)

# =====================================================================
# Memory guards
Expand Down Expand Up @@ -322,7 +315,7 @@ def _fill_cupy(dem_data):
# =====================================================================

def _build_constraint_ring(iy, ix, boundaries, chunks_y, chunks_x,
n_tile_y, n_tile_x):
n_tile_y, n_tile_x):
"""Build (h+2, w+2) constraint ring for tile (iy, ix).

Grid boundaries use -1e308 (water escapes at DEM elevation).
Expand Down Expand Up @@ -378,7 +371,7 @@ def _build_constraint_ring(iy, ix, boundaries, chunks_y, chunks_x,


def _process_fill_tile(iy, ix, dem_da, boundaries,
chunks_y, chunks_x, n_tile_y, n_tile_x):
chunks_y, chunks_x, n_tile_y, n_tile_x):
"""Run P-D on one tile; update boundaries in-place.

Returns the maximum absolute boundary change (float).
Expand All @@ -388,8 +381,8 @@ def _process_fill_tile(iy, ix, dem_da, boundaries,
h, w = chunk.shape

ring = _build_constraint_ring(iy, ix, boundaries,
chunks_y, chunks_x,
n_tile_y, n_tile_x)
chunks_y, chunks_x,
n_tile_y, n_tile_x)

result = _fill_tile_kernel(chunk, h, w, ring)

Expand Down Expand Up @@ -488,13 +481,13 @@ def _fill_dask_iterative(dem_da):
boundaries = boundaries.snapshot()

return _assemble_fill_result(dem_da, boundaries,
chunks_y, chunks_x,
n_tile_y, n_tile_x)
chunks_y, chunks_x,
n_tile_y, n_tile_x)


def _assemble_fill_result(dem_da, boundaries,
chunks_y, chunks_x,
n_tile_y, n_tile_x):
chunks_y, chunks_x,
n_tile_y, n_tile_x):
"""Build lazy dask array by re-running tiles with converged constraints."""

def _tile_fn(dem_block, block_info=None):
Expand All @@ -503,8 +496,8 @@ def _tile_fn(dem_block, block_info=None):
iy, ix = block_info[0]['chunk-location']
h, w = dem_block.shape
ring = _build_constraint_ring(iy, ix, boundaries,
chunks_y, chunks_x,
n_tile_y, n_tile_x)
chunks_y, chunks_x,
n_tile_y, n_tile_x)
return _fill_tile_kernel(
np.asarray(dem_block, dtype=np.float64), h, w, ring)

Expand Down Expand Up @@ -536,8 +529,8 @@ def _fill_dask_cupy(dem_data):

@supports_dataset
def fill_d8(dem: xr.DataArray,
z_limit=None,
name: str = 'fill') -> xr.DataArray:
z_limit=None,
name: str = 'fill') -> xr.DataArray:
"""Fill depressions in a DEM using Planchon-Darboux iterative flooding.

Raises each depression cell to the elevation of its pour point
Expand Down
33 changes: 13 additions & 20 deletions xrspatial/hydro/flow_accumulation_d8.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,10 @@ class cupy: # type: ignore[no-redef]
except ImportError:
da = None

from xrspatial.utils import (
_validate_raster,
cuda_args,
has_cuda_and_cupy,
is_cupy_array,
is_dask_cupy,
ngjit,
)
from xrspatial.hydro._boundary_store import BoundaryStore
from xrspatial.dataset_support import supports_dataset

from xrspatial.hydro._boundary_store import BoundaryStore
from xrspatial.utils import (_validate_raster, cuda_args, has_cuda_and_cupy, is_cupy_array,
is_dask_cupy, ngjit)

# =====================================================================
# Memory guards
Expand Down Expand Up @@ -459,8 +452,8 @@ def _flow_accum_cupy(flow_dir_data):


def _flow_accum_tile_cupy(flow_dir_data,
seed_top, seed_bottom, seed_left, seed_right,
seed_tl, seed_tr, seed_bl, seed_br):
seed_top, seed_bottom, seed_left, seed_right,
seed_tl, seed_tr, seed_bl, seed_br):
"""GPU seeded flow accumulation for a single tile.

Same algorithm as ``_flow_accum_cupy`` but injects external seed
Expand Down Expand Up @@ -860,7 +853,7 @@ def _tile_fn(flow_dir_block, block_info=None):


def _process_tile_cupy(iy, ix, flow_dir_da, boundaries, flow_bdry,
chunks_y, chunks_x, n_tile_y, n_tile_x):
chunks_y, chunks_x, n_tile_y, n_tile_x):
"""Run seeded GPU flow accumulation on one tile; update boundaries."""
import cupy as cp

Expand Down Expand Up @@ -899,7 +892,7 @@ def _process_tile_cupy(iy, ix, flow_dir_da, boundaries, flow_bdry,


def _assemble_result_cupy(flow_dir_da, boundaries, flow_bdry,
chunks_y, chunks_x, n_tile_y, n_tile_x):
chunks_y, chunks_x, n_tile_y, n_tile_x):
"""Build a lazy dask+cupy array using GPU tile kernel."""
import cupy as cp

Expand Down Expand Up @@ -941,16 +934,16 @@ def _flow_accum_dask_cupy(flow_dir_da):
for iy in range(n_tile_y):
for ix in range(n_tile_x):
c = _process_tile_cupy(iy, ix, flow_dir_da, boundaries,
flow_bdry, chunks_y, chunks_x,
n_tile_y, n_tile_x)
flow_bdry, chunks_y, chunks_x,
n_tile_y, n_tile_x)
if c > max_change:
max_change = c

for iy in reversed(range(n_tile_y)):
for ix in reversed(range(n_tile_x)):
c = _process_tile_cupy(iy, ix, flow_dir_da, boundaries,
flow_bdry, chunks_y, chunks_x,
n_tile_y, n_tile_x)
flow_bdry, chunks_y, chunks_x,
n_tile_y, n_tile_x)
if c > max_change:
max_change = c

Expand All @@ -960,7 +953,7 @@ def _flow_accum_dask_cupy(flow_dir_da):
boundaries = boundaries.snapshot()

return _assemble_result_cupy(flow_dir_da, boundaries, flow_bdry,
chunks_y, chunks_x, n_tile_y, n_tile_x)
chunks_y, chunks_x, n_tile_y, n_tile_x)


# =====================================================================
Expand All @@ -969,7 +962,7 @@ def _flow_accum_dask_cupy(flow_dir_da):

@supports_dataset
def flow_accumulation_d8(flow_dir: xr.DataArray,
name: str = 'flow_accumulation') -> xr.DataArray:
name: str = 'flow_accumulation') -> xr.DataArray:
"""Compute flow accumulation from a D8 flow direction grid.

Each cell drains to exactly one downstream neighbor based on
Expand Down
17 changes: 6 additions & 11 deletions xrspatial/hydro/flow_direction_d8.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,16 @@ class cupy(object):
import xarray as xr
from numba import cuda

from xrspatial.utils import ArrayTypeFunctionMapping
from xrspatial.utils import _boundary_to_dask
from xrspatial.utils import _pad_array
from xrspatial.utils import _validate_boundary
from xrspatial.utils import _validate_raster
from xrspatial.utils import cuda_args
from xrspatial.utils import get_dataarray_resolution
from xrspatial.utils import ngjit
from xrspatial.dataset_support import supports_dataset

from xrspatial.utils import (ArrayTypeFunctionMapping, _boundary_to_dask, _pad_array,
_validate_boundary, _validate_raster, cuda_args,
get_dataarray_resolution, ngjit)

# =====================================================================
# CPU kernel
# =====================================================================


@ngjit
def _cpu(data, cellsize_x, cellsize_y):
out = np.empty(data.shape, np.float64)
Expand Down Expand Up @@ -267,8 +262,8 @@ def _run_dask_cupy(data: da.Array,

@supports_dataset
def flow_direction_d8(agg: xr.DataArray,
name: str = 'flow_direction',
boundary: str = 'nan') -> xr.DataArray:
name: str = 'flow_direction',
boundary: str = 'nan') -> xr.DataArray:
"""Compute D8 flow direction for each cell.

Determines which of the 8 neighbors has the steepest downhill
Expand Down
47 changes: 19 additions & 28 deletions xrspatial/hydro/flow_length_d8.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,12 @@
except ImportError:
da = None

from xrspatial.dataset_support import supports_dataset
from xrspatial.hydro._boundary_store import BoundaryStore
from xrspatial.hydro.flow_accumulation_d8 import _code_to_offset
from xrspatial.hydro.watershed_d8 import _code_to_offset_py
from xrspatial.hydro._boundary_store import BoundaryStore
from xrspatial.utils import (
_validate_raster,
get_dataarray_resolution,
has_cuda_and_cupy,
is_cupy_array,
is_dask_cupy,
ngjit,
)
from xrspatial.dataset_support import supports_dataset

from xrspatial.utils import (_validate_raster, get_dataarray_resolution, has_cuda_and_cupy,
is_cupy_array, is_dask_cupy, ngjit)

# =====================================================================
# Memory guards
Expand Down Expand Up @@ -314,8 +307,8 @@ def _flow_length_cupy(flow_dir_data, direction, cellsize_x, cellsize_y, diag):

@ngjit
def _flow_length_downstream_tile(flow_dir, h, w, cellsize_x, cellsize_y, diag,
exit_top, exit_bottom, exit_left, exit_right,
exit_tl, exit_tr, exit_bl, exit_br):
exit_top, exit_bottom, exit_left, exit_right,
exit_tl, exit_tr, exit_bl, exit_br):
"""Downstream flow length for a single tile with exit-label seeds.

Boundary cells that flow out of the tile use exit values as the
Expand Down Expand Up @@ -489,8 +482,8 @@ def _flow_length_downstream_tile(flow_dir, h, w, cellsize_x, cellsize_y, diag,

@ngjit
def _flow_length_upstream_tile(flow_dir, h, w, cellsize_x, cellsize_y, diag,
seed_top, seed_bottom, seed_left, seed_right,
seed_tl, seed_tr, seed_bl, seed_br):
seed_top, seed_bottom, seed_left, seed_right,
seed_tl, seed_tr, seed_bl, seed_br):
"""Upstream flow length for a single tile with entry seeds.

Boundary cells that receive flow from outside the tile get seeded
Expand Down Expand Up @@ -603,7 +596,7 @@ def _preprocess_tiles(flow_dir_da, chunks_y, chunks_x):


def _compute_exit_labels_downstream(iy, ix, boundaries, flow_bdry,
chunks_y, chunks_x, n_tile_y, n_tile_x):
chunks_y, chunks_x, n_tile_y, n_tile_x):
"""For downstream: look up the flow_length of the cell each
boundary cell flows TO in the adjacent tile."""
tile_h = chunks_y[iy]
Expand Down Expand Up @@ -770,12 +763,10 @@ def _compute_exit_labels_downstream(iy, ix, boundaries, flow_bdry,


def _compute_seeds_upstream(iy, ix, boundaries, flow_bdry,
chunks_y, chunks_x, n_tile_y, n_tile_x,
cellsize_x, cellsize_y, diag):
chunks_y, chunks_x, n_tile_y, n_tile_x,
cellsize_x, cellsize_y, diag):
"""For upstream: check which adjacent cells flow INTO this tile,
and seed with max(existing, neighbor_upstream + step_dist)."""
from xrspatial.hydro.flow_accumulation_d8 import _compute_seeds as _compute_accum_seeds

tile_h = chunks_y[iy]
tile_w = chunks_x[ix]

Expand Down Expand Up @@ -922,8 +913,8 @@ def _compute_seeds_upstream(iy, ix, boundaries, flow_bdry,


def _process_tile_downstream(iy, ix, flow_dir_da, boundaries, flow_bdry,
chunks_y, chunks_x, n_tile_y, n_tile_x,
cellsize_x, cellsize_y, diag):
chunks_y, chunks_x, n_tile_y, n_tile_x,
cellsize_x, cellsize_y, diag):
"""Process one tile for downstream flow length; update boundaries."""
chunk = np.asarray(
flow_dir_da.blocks[iy, ix].compute(), dtype=np.float64)
Expand Down Expand Up @@ -961,8 +952,8 @@ def _process_tile_downstream(iy, ix, flow_dir_da, boundaries, flow_bdry,


def _process_tile_upstream(iy, ix, flow_dir_da, boundaries, flow_bdry,
chunks_y, chunks_x, n_tile_y, n_tile_x,
cellsize_x, cellsize_y, diag):
chunks_y, chunks_x, n_tile_y, n_tile_x,
cellsize_x, cellsize_y, diag):
"""Process one tile for upstream flow length; update boundaries."""
chunk = np.asarray(
flow_dir_da.blocks[iy, ix].compute(), dtype=np.float64)
Expand Down Expand Up @@ -1001,7 +992,7 @@ def _process_tile_upstream(iy, ix, flow_dir_da, boundaries, flow_bdry,


def _flow_length_dask_iterative(flow_dir_da, direction,
cellsize_x, cellsize_y, diag):
cellsize_x, cellsize_y, diag):
"""Iterative boundary-propagation for flow length on dask arrays."""
chunks_y = flow_dir_da.chunks[0]
chunks_x = flow_dir_da.chunks[1]
Expand Down Expand Up @@ -1087,7 +1078,7 @@ def _tile_fn(flow_dir_block, block_info=None):


def _flow_length_dask_cupy(flow_dir_da, direction,
cellsize_x, cellsize_y, diag):
cellsize_x, cellsize_y, diag):
"""Dask+CuPy: convert to numpy, run CPU iterative path, convert back."""
import cupy as cp

Expand All @@ -1109,8 +1100,8 @@ def _flow_length_dask_cupy(flow_dir_da, direction,

@supports_dataset
def flow_length_d8(flow_dir: xr.DataArray,
direction: str = 'downstream',
name: str = 'flow_length') -> xr.DataArray:
direction: str = 'downstream',
name: str = 'flow_length') -> xr.DataArray:
"""Compute D8 flow length from a flow direction grid.

Parameters
Expand Down
Loading
Loading