xarray-spatial 0.9.4__tar.gz → 0.9.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xarray_spatial-0.9.6/.claude/accuracy-sweep-state.json +9 -0
- xarray_spatial-0.9.6/.claude/commands/accuracy-sweep.md +158 -0
- xarray_spatial-0.9.6/.claude/commands/sweep-performance.md +494 -0
- xarray_spatial-0.9.6/.claude/performance-sweep-state.json +47 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.github/workflows/benchmarks.yml +2 -2
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/CHANGELOG.md +31 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/PKG-INFO +40 -26
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/README.md +39 -25
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/setup.cfg +11 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xarray_spatial.egg-info/PKG-INFO +40 -26
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xarray_spatial.egg-info/SOURCES.txt +9 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/__init__.py +4 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/_version.py +3 -3
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/accessor.py +12 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/aspect.py +12 -4
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/balanced_allocation.py +34 -6
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/classify.py +2 -2
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/cost_distance.py +33 -6
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/dasymetric.py +16 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/diffusion.py +90 -28
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/erosion.py +22 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/normalize.py +22 -33
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/preview.py +3 -1
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/rasterize.py +4 -1
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/__init__.py +13 -0
- xarray_spatial-0.9.6/xrspatial/sieve.py +480 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/surface_distance.py +14 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_balanced_allocation.py +32 -0
- xarray_spatial-0.9.6/xrspatial/tests/test_dask_laziness.py +180 -0
- xarray_spatial-0.9.6/xrspatial/tests/test_sieve.py +502 -0
- xarray_spatial-0.9.6/xrspatial/tests/test_visibility.py +276 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_zonal.py +37 -1
- xarray_spatial-0.9.6/xrspatial/visibility.py +256 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/zonal.py +33 -13
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/backend-parity.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/bench.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/dask-notebook.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/efficiency-audit.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/new-issues.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/release-major.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/release-minor.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/release-patch.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/review-pr.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/rockout.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/user-guide-notebook.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.claude/commands/validate.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.gitattributes +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.github/ISSUE_TEMPLATE/feature-proposal.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.github/labeler.yml +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.github/pull_request_template.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.github/workflows/labeler.yml +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.github/workflows/pypi-publish.yml +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.github/workflows/test.yml +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.gitignore +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/.readthedocs.yml +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/CODE_OF_CONDUCT.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/CONTRIBUTING.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/Citation-styles.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/LICENSE.txt +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/MANIFEST.in +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/RELEASE.md +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/codecov.yml +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/pyproject.toml +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/setup.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xarray_spatial.egg-info/dependency_links.txt +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xarray_spatial.egg-info/entry_points.txt +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xarray_spatial.egg-info/not-zip-safe +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xarray_spatial.egg-info/requires.txt +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xarray_spatial.egg-info/top_level.txt +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/__main__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/analytics.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/bilateral.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/bump.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/contour.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/convolution.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/corridor.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/curvature.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/dataset_support.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/datasets/__init__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/datasets/sentinel-2/blue_band.nc +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/datasets/sentinel-2/green_band.nc +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/datasets/sentinel-2/nir_band.nc +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/datasets/sentinel-2/red_band.nc +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/datasets/sentinel-2/swir1_band.nc +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/datasets/sentinel-2/swir2_band.nc +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/diagnostics.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/edge_detection.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/emerging_hotspots.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/experimental/__init__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/experimental/min_observable_height.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/fire.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/flood.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/focal.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geodesic.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/__init__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/_compression.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/_dtypes.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/_geotags.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/_gpu_decode.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/_header.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/_reader.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/_vrt.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/_writer.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/__init__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/bench_vs_rioxarray.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/conftest.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_accessor_io.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_accuracy_1081.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_cog.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_compression.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_compression_level.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_dtype_read.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_edge_cases.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_features.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_geotags.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_header.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_jpeg.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_jpeg2000.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_lerc.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_lz4.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_reader.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_streaming_write.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_vrt_write.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/geotiff/tests/test_writer.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/glcm.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/gpu_rtx/__init__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/gpu_rtx/cuda_utils.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/gpu_rtx/hillshade.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/gpu_rtx/mesh_utils.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/gpu_rtx/viewshed.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hillshade.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/__init__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/_boundary_store.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/basin_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/fill_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_accumulation_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_accumulation_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_accumulation_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_direction_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_direction_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_direction_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_length_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_length_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_length_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_path_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_path_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/flow_path_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/hand_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/hand_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/hand_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/sink_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/snap_pour_point_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/stream_link_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/stream_link_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/stream_link_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/stream_order_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/stream_order_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/stream_order_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/__init__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/conftest.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_basin_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_fill_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_accumulation_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_accumulation_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_accumulation_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_direction_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_direction_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_direction_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_length_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_length_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_length_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_path_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_path_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_flow_path_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_hand_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_hand_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_hand_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_sink_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_snap_pour_point_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_stream_link_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_stream_link_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_stream_link_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_stream_order_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_stream_order_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_stream_order_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_twi_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_watershed_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_watershed_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/tests/test_watershed_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/twi_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/watershed_d8.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/watershed_dinf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/hydro/watershed_mfd.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/interpolate/__init__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/interpolate/_idw.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/interpolate/_kriging.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/interpolate/_spline.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/interpolate/_validation.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/mahalanobis.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/mcda/__init__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/mcda/combine.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/mcda/constrain.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/mcda/sensitivity.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/mcda/standardize.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/mcda/weights.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/morphology.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/multispectral.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/pathfinding.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/perlin.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/polygonize.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/proximity.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_crs_utils.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_datum_grids.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_grid.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_interpolate.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_itrf.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_lite_crs.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_merge.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_projections.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_projections_cuda.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_transform.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/_vertical.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/at_bev_AT_GIS_GRID.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/au_icsm_A66_National_13_09_01.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/be_ign_bd72lb72_etrs89lb08.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/ch_swisstopo_CHENyx06_ETRS.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/de_adv_BETA2007.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/es_ign_SPED2ETV2.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/nl_nsgi_rdcorr2018.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/pt_dgt_D73_ETRS89_geo.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/uk_os_OSTN15_NTv2_OSGBtoETRS.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/us_nga_egm96_15.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/us_noaa_alaska.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/us_noaa_conus.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/us_noaa_hawaii.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/us_noaa_nadcon5_nad27_nad83_1986_conus.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/reproject/grids/us_noaa_prvi.tif +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/sky_view_factor.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/slope.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/terrain.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/terrain_metrics.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/__init__.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/bench_reproject_vs_rioxarray.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/conftest.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/general_checks.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_accessor.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_analytics.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_aspect.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_bilateral.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_bump.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_classify.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_contour.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_corridor.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_cost_distance.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_curvature.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_dask_cupy_gaps.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_dasymetric.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_dataset_support.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_datasets.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_diagnostics.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_diffusion.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_edge_detection.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_emerging_hotspots.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_erosion.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_fire.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_flood.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_focal.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_fused_overlap.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_geodesic_aspect.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_geodesic_slope.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_glcm.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_glcm_metric_order.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_hillshade.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_interpolation.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_lite_crs.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_mahalanobis.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_mcda.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_min_observable_height.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_morphology.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_morphology_derived.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_multi_overlap.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_multispectral.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_normalize.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_northness_eastness.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_pathfinding.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_perlin.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_polygonize.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_preview.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_proximity.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_rasterize.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_rasterize_accuracy.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_rechunk_no_shuffle.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_reproject.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_sky_view_factor.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_slope.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_surface_distance.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_terrain.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_terrain_metrics.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_utils.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_validation.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/tests/test_viewshed.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/utils.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/viewshed.py +0 -0
- {xarray_spatial-0.9.4 → xarray_spatial-0.9.6}/xrspatial/worley.py +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"inspections": {
|
|
3
|
+
"zonal": { "last_inspected": "2026-03-30T12:00:00Z", "issue": 1090 },
|
|
4
|
+
"focal": { "last_inspected": "2026-03-30T13:00:00Z", "issue": 1092 },
|
|
5
|
+
"multispectral": { "last_inspected": "2026-03-30T14:00:00Z", "issue": 1094 },
|
|
6
|
+
"proximity": { "last_inspected": "2026-03-30T15:00:00Z", "issue": null, "notes": "Direction >= boundary fragile but works due to truncated constant. Float32 truncation is design choice. No wrong-results bugs found." },
|
|
7
|
+
"curvature": { "last_inspected": "2026-03-30T15:00:00Z", "issue": null, "notes": "Formula matches ArcGIS reference. Backends consistent. No issues found." }
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Accuracy Sweep: Generate a Ralph Loop targeting under-inspected modules
|
|
2
|
+
|
|
3
|
+
Analyze xrspatial modules by recency and inspection history, then print a
|
|
4
|
+
ready-to-run `/ralph-loop` command that targets the highest-priority modules.
|
|
5
|
+
|
|
6
|
+
Optional arguments: $ARGUMENTS
|
|
7
|
+
(e.g. `--top 5`, `--exclude slope,aspect`, `--only-terrain`, `--reset-state`)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Step 1 -- Gather module metadata via git
|
|
12
|
+
|
|
13
|
+
For every `.py` file directly under `xrspatial/` (skip `__init__.py`,
|
|
14
|
+
`_version.py`, `__main__.py`, `utils.py`, `accessor.py`, `preview.py`,
|
|
15
|
+
`dataset_support.py`, `diagnostics.py`, `analytics.py`), collect:
|
|
16
|
+
|
|
17
|
+
| Field | How |
|
|
18
|
+
|-------|-----|
|
|
19
|
+
| **last_modified** | `git log -1 --format=%aI -- xrspatial/<module>.py` |
|
|
20
|
+
| **first_commit** | `git log --diff-filter=A --format=%aI -- xrspatial/<module>.py` |
|
|
21
|
+
| **total_commits** | `git log --oneline -- xrspatial/<module>.py \| wc -l` |
|
|
22
|
+
| **recent_accuracy_commits** | `git log --oneline --grep='accuracy\|precision\|numerical\|geodesic' -- xrspatial/<module>.py` |
|
|
23
|
+
|
|
24
|
+
Store results in a temporary variable -- do NOT write intermediate files.
|
|
25
|
+
|
|
26
|
+
## Step 2 -- Load inspection state
|
|
27
|
+
|
|
28
|
+
Read the state file at `.claude/accuracy-sweep-state.json`.
|
|
29
|
+
|
|
30
|
+
If it does not exist, treat every module as never-inspected.
|
|
31
|
+
|
|
32
|
+
If `$ARGUMENTS` contains `--reset-state`, delete the file and treat
|
|
33
|
+
everything as never-inspected.
|
|
34
|
+
|
|
35
|
+
The state file schema:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"inspections": {
|
|
40
|
+
"slope": { "last_inspected": "2026-03-28T14:00:00Z", "issue": 1042 },
|
|
41
|
+
"aspect": { "last_inspected": "2026-03-28T15:30:00Z", "issue": 1043 }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Step 3 -- Score each module
|
|
47
|
+
|
|
48
|
+
Compute a priority score for each module. Higher = more urgent.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
days_since_inspected = (today - last_inspected).days # 9999 if never inspected
|
|
52
|
+
days_since_modified = (today - last_modified).days
|
|
53
|
+
total_commits = from Step 1
|
|
54
|
+
has_recent_accuracy_work = 1 if recent_accuracy_commits is non-empty, else 0
|
|
55
|
+
|
|
56
|
+
score = (days_since_inspected * 3)
|
|
57
|
+
+ (total_commits * 0.5)
|
|
58
|
+
- (days_since_modified * 0.2)
|
|
59
|
+
- (has_recent_accuracy_work * 500)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Rationale:
|
|
63
|
+
- Modules never inspected dominate (9999 * 3)
|
|
64
|
+
- More commits = more complex = more likely to have bugs
|
|
65
|
+
- Recently modified modules slightly deprioritized (someone just touched them)
|
|
66
|
+
- Modules with existing accuracy work heavily deprioritized
|
|
67
|
+
|
|
68
|
+
## Step 4 -- Apply filters from $ARGUMENTS
|
|
69
|
+
|
|
70
|
+
- `--top N` -- only include the top N modules (default: 5)
|
|
71
|
+
- `--exclude mod1,mod2` -- remove named modules from the list
|
|
72
|
+
- `--only-terrain` -- restrict to slope, aspect, curvature, terrain,
|
|
73
|
+
terrain_metrics, hillshade, sky_view_factor
|
|
74
|
+
- `--only-focal` -- restrict to focal, convolution, morphology, bilateral,
|
|
75
|
+
edge_detection, glcm
|
|
76
|
+
- `--only-hydro` -- restrict to flood, cost_distance, geodesic,
|
|
77
|
+
surface_distance, viewshed, erosion, diffusion
|
|
78
|
+
|
|
79
|
+
## Step 5 -- Print the results
|
|
80
|
+
|
|
81
|
+
### 5a. Print the ranked table
|
|
82
|
+
|
|
83
|
+
Print a markdown table showing ALL scored modules (not just the selected ones),
|
|
84
|
+
sorted by score descending:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
| Rank | Module | Score | Last Inspected | Last Modified | Commits |
|
|
88
|
+
|------|-----------------|--------|----------------|---------------|---------|
|
|
89
|
+
| 1 | viewshed | 30012 | never | 45 days ago | 23 |
|
|
90
|
+
| 2 | flood | 29998 | never | 120 days ago | 18 |
|
|
91
|
+
| ... | ... | ... | ... | ... | ... |
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 5b. Print the generated ralph-loop command
|
|
95
|
+
|
|
96
|
+
Using the top N modules from the ranked list, generate and print a command
|
|
97
|
+
like this (adapt the module list to actual results):
|
|
98
|
+
|
|
99
|
+
````
|
|
100
|
+
/ralph-loop "Survey xarray-spatial modules for numerical accuracy issues.
|
|
101
|
+
|
|
102
|
+
**Target these modules in priority order:**
|
|
103
|
+
1. viewshed (xrspatial/viewshed.py) -- never inspected, 23 commits
|
|
104
|
+
2. flood (xrspatial/flood.py) -- never inspected, 18 commits
|
|
105
|
+
3. focal (xrspatial/focal.py) -- never inspected, 31 commits
|
|
106
|
+
4. erosion (xrspatial/erosion.py) -- never inspected, 12 commits
|
|
107
|
+
5. classify (xrspatial/classify.py) -- never inspected, 9 commits
|
|
108
|
+
|
|
109
|
+
**For each module, in order:**
|
|
110
|
+
1. Read the source and identify potential accuracy issues:
|
|
111
|
+
- Floating point precision loss
|
|
112
|
+
- Incorrect NaN propagation
|
|
113
|
+
- Off-by-one errors in neighborhood operations
|
|
114
|
+
- Missing or wrong Earth curvature corrections
|
|
115
|
+
- Backend inconsistencies (numpy vs cupy vs dask results differ)
|
|
116
|
+
2. Run /rockout to fix the issue end-to-end (issue, worktree, fix, tests, docs)
|
|
117
|
+
3. After completing rockout for ONE module, output <promise>ITERATION DONE</promise>
|
|
118
|
+
|
|
119
|
+
If you find no accuracy issues in the current target module, skip it and move
|
|
120
|
+
to the next one.
|
|
121
|
+
|
|
122
|
+
If all target modules have been addressed or have no issues, output
|
|
123
|
+
<promise>ALL ACCURACY ISSUES FIXED</promise>." --max-iterations {N} --completion-promise "ALL ACCURACY ISSUES FIXED"
|
|
124
|
+
````
|
|
125
|
+
|
|
126
|
+
Set `--max-iterations` to the number of target modules + 2 (buffer for retries).
|
|
127
|
+
|
|
128
|
+
### 5c. Print a reminder
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
To run this sweep: copy the command above and paste it.
|
|
132
|
+
To update state after a manual rockout: edit .claude/accuracy-sweep-state.json
|
|
133
|
+
To reset all tracking: /accuracy-sweep --reset-state
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Step 6 -- Update state (ONLY when called from inside a ralph-loop)
|
|
137
|
+
|
|
138
|
+
This step is informational. The accuracy-sweep command itself does NOT update
|
|
139
|
+
the state file. State is updated when `/rockout` completes -- the rockout
|
|
140
|
+
workflow should append to `.claude/accuracy-sweep-state.json` after creating
|
|
141
|
+
the issue.
|
|
142
|
+
|
|
143
|
+
To enable this, print a note reminding the user that after each rockout
|
|
144
|
+
iteration completes, they can manually record the inspection:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
// Add to .claude/accuracy-sweep-state.json after each rockout:
|
|
148
|
+
{ "module_name": { "last_inspected": "ISO-DATE", "issue": ISSUE_NUMBER } }
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## General Rules
|
|
154
|
+
|
|
155
|
+
- Do NOT modify any source files. This command is read-only analysis.
|
|
156
|
+
- Do NOT create GitHub issues. This command only generates the ralph-loop command.
|
|
157
|
+
- Keep the output concise -- the table and command are the deliverables.
|
|
158
|
+
- If $ARGUMENTS is empty, use defaults: top 5, no category filter, no exclusions.
|
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
# Performance Sweep: Parallel Triage and Fix Workflow
|
|
2
|
+
|
|
3
|
+
Audit xrspatial modules for performance bottlenecks, OOM risk under 30TB dask
|
|
4
|
+
workloads, and backend-specific anti-patterns. Dispatches parallel subagents
|
|
5
|
+
for fast triage, then generates a ralph-loop to benchmark and fix HIGH-severity
|
|
6
|
+
issues.
|
|
7
|
+
|
|
8
|
+
Optional arguments: $ARGUMENTS
|
|
9
|
+
(e.g. `--top 5`, `--exclude slope,aspect`, `--only-io`, `--reset-state`)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Step 0 -- Determine mode and parse arguments
|
|
14
|
+
|
|
15
|
+
Parse $ARGUMENTS for these flags (multiple may combine):
|
|
16
|
+
|
|
17
|
+
| Flag | Effect |
|
|
18
|
+
|------|--------|
|
|
19
|
+
| `--top N` | Limit Phase 1 to the top N scored modules (default: all) |
|
|
20
|
+
| `--exclude mod1,mod2` | Remove named modules from scope |
|
|
21
|
+
| `--only-terrain` | Restrict to: slope, aspect, curvature, terrain, terrain_metrics, hillshade, sky_view_factor |
|
|
22
|
+
| `--only-focal` | Restrict to: focal, convolution, morphology, bilateral, edge_detection, glcm |
|
|
23
|
+
| `--only-hydro` | Restrict to: flood, cost_distance, geodesic, surface_distance, viewshed, erosion, diffusion |
|
|
24
|
+
| `--only-io` | Restrict to: geotiff, reproject, rasterize, polygonize |
|
|
25
|
+
| `--reset-state` | Delete `.claude/performance-sweep-state.json` and treat all modules as never-inspected |
|
|
26
|
+
| `--skip-phase1` | Skip triage; reuse last state file; go straight to ralph-loop generation for unresolved HIGH items |
|
|
27
|
+
| `--report-only` | Run Phase 1 triage but do not generate a ralph-loop command |
|
|
28
|
+
| `--size small` | Phase 2 benchmarks use 128x128 arrays |
|
|
29
|
+
| `--size large` | Phase 2 benchmarks use 2048x2048 arrays |
|
|
30
|
+
| `--high-only` | Only report HIGH severity findings in the triage output |
|
|
31
|
+
|
|
32
|
+
If `--skip-phase1` is set, jump to Step 6 (ralph-loop generation).
|
|
33
|
+
Otherwise proceed to Step 1.
|
|
34
|
+
|
|
35
|
+
## Step 1 -- Discover modules in scope
|
|
36
|
+
|
|
37
|
+
Enumerate all candidate modules. For each, record its file path(s):
|
|
38
|
+
|
|
39
|
+
**Single-file modules:** Every `.py` file directly under `xrspatial/`, excluding
|
|
40
|
+
`__init__.py`, `_version.py`, `__main__.py`, `utils.py`, `accessor.py`,
|
|
41
|
+
`preview.py`, `dataset_support.py`, `diagnostics.py`, `analytics.py`.
|
|
42
|
+
|
|
43
|
+
**Subpackage modules:** The `geotiff/` and `reproject/` directories under
|
|
44
|
+
`xrspatial/`. Treat each subpackage as a single audit unit. List all `.py`
|
|
45
|
+
files within each (excluding `__init__.py`).
|
|
46
|
+
|
|
47
|
+
Apply `--only-*` and `--exclude` filters from Step 0 to narrow the list.
|
|
48
|
+
|
|
49
|
+
Store the filtered module list in memory (do NOT write intermediate files).
|
|
50
|
+
|
|
51
|
+
## Step 2 -- Gather metadata and score each module
|
|
52
|
+
|
|
53
|
+
For every module in scope, collect:
|
|
54
|
+
|
|
55
|
+
| Field | How |
|
|
56
|
+
|-------|-----|
|
|
57
|
+
| **last_modified** | `git log -1 --format=%aI -- <path>` (for subpackages, use the most recent file) |
|
|
58
|
+
| **total_commits** | `git log --oneline -- <path> \| wc -l` |
|
|
59
|
+
| **loc** | `wc -l < <path>` (for subpackages, sum all files) |
|
|
60
|
+
| **has_dask_backend** | grep the file(s) for `_run_dask`, `map_overlap`, `map_blocks` |
|
|
61
|
+
| **has_cuda_backend** | grep the file(s) for `@cuda.jit`, `import cupy` |
|
|
62
|
+
| **is_io_module** | module is geotiff or reproject |
|
|
63
|
+
| **has_existing_bench** | a file matching the module name exists in `benchmarks/benchmarks/` |
|
|
64
|
+
|
|
65
|
+
### Load inspection state
|
|
66
|
+
|
|
67
|
+
Read `.claude/performance-sweep-state.json`. If it does not exist, treat every
|
|
68
|
+
module as never-inspected. If `--reset-state` was set, delete the file first.
|
|
69
|
+
|
|
70
|
+
State file schema:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"last_triage": "ISO-DATE",
|
|
75
|
+
"modules": {
|
|
76
|
+
"slope": {
|
|
77
|
+
"last_inspected": "ISO-DATE",
|
|
78
|
+
"oom_verdict": "SAFE",
|
|
79
|
+
"bottleneck": "compute-bound",
|
|
80
|
+
"high_count": 0,
|
|
81
|
+
"issue": null
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Compute scores
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
days_since_inspected = (today - last_perf_inspected).days # 9999 if never
|
|
91
|
+
days_since_modified = (today - last_modified).days
|
|
92
|
+
|
|
93
|
+
score = (days_since_inspected * 3)
|
|
94
|
+
+ (loc * 0.1)
|
|
95
|
+
+ (total_commits * 0.5)
|
|
96
|
+
+ (has_dask_backend * 200)
|
|
97
|
+
+ (has_cuda_backend * 150)
|
|
98
|
+
+ (is_io_module * 300)
|
|
99
|
+
- (days_since_modified * 0.2)
|
|
100
|
+
- (has_existing_bench * 100)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Sort modules by score descending. If `--top N` is set, keep only the top N.
|
|
104
|
+
|
|
105
|
+
## Step 3 -- Dispatch parallel subagents for static triage
|
|
106
|
+
|
|
107
|
+
For each module in the scored list, dispatch a subagent using the Agent tool.
|
|
108
|
+
Launch ALL subagents in a single message (parallel dispatch). Each subagent
|
|
109
|
+
receives the prompt below, with `MODULE_NAME` and `MODULE_FILES` substituted.
|
|
110
|
+
|
|
111
|
+
**Subagent prompt template:**
|
|
112
|
+
|
|
113
|
+
~~~
|
|
114
|
+
You are auditing the xrspatial module "MODULE_NAME" for performance issues.
|
|
115
|
+
|
|
116
|
+
Read these files: MODULE_FILES
|
|
117
|
+
|
|
118
|
+
Perform ALL of the following analyses and return your findings as a single
|
|
119
|
+
JSON object. Do NOT modify any files. This is read-only analysis.
|
|
120
|
+
|
|
121
|
+
### 1. Dask Path Analysis
|
|
122
|
+
|
|
123
|
+
Trace every dask code path (_run_dask, _run_dask_cupy, or any function that
|
|
124
|
+
receives dask-backed DataArrays). Flag these patterns with severity:
|
|
125
|
+
|
|
126
|
+
- HIGH: `.values` on a dask-backed DataArray or CuPy array (premature materialization)
|
|
127
|
+
- HIGH: `.compute()` inside a loop (materializes full graph each iteration)
|
|
128
|
+
- HIGH: `np.array()` or `np.asarray()` wrapping a dask or CuPy array
|
|
129
|
+
- MEDIUM: `da.stack()` without a following `.rechunk()`
|
|
130
|
+
- MEDIUM: `map_overlap` with depth >= chunk_size / 4
|
|
131
|
+
- MEDIUM: Missing `boundary` argument in `map_overlap`
|
|
132
|
+
- MEDIUM: Same function called twice on same input without caching
|
|
133
|
+
- MEDIUM: Python `for` loop iterating over dask chunks (serializes the graph)
|
|
134
|
+
|
|
135
|
+
If the module has NO dask code path, note "no dask backend" and skip.
|
|
136
|
+
|
|
137
|
+
### 2. 30TB / 16GB OOM Verdict
|
|
138
|
+
|
|
139
|
+
For each dask code path found in section 1:
|
|
140
|
+
|
|
141
|
+
**Part A — Static trace:** Follow the code end-to-end. Answer: does peak
|
|
142
|
+
memory scale with total array size, or with chunk size? If any operation
|
|
143
|
+
forces full materialization, the verdict is WILL OOM.
|
|
144
|
+
|
|
145
|
+
**Part B — Task graph simulation:** Write and run a Python script (in /tmp/
|
|
146
|
+
with a unique name including "MODULE_NAME") that:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
import dask.array as da
|
|
150
|
+
import xarray as xr
|
|
151
|
+
import json, sys
|
|
152
|
+
|
|
153
|
+
arr = da.zeros((2560, 2560), chunks=(256, 256), dtype='float64')
|
|
154
|
+
raster = xr.DataArray(arr, dims=['y', 'x'])
|
|
155
|
+
|
|
156
|
+
# Add coords if the function needs them (geodesic, slope with CRS, etc.)
|
|
157
|
+
# raster = raster.assign_coords(x=np.linspace(-180, 180, 2560),
|
|
158
|
+
# y=np.linspace(-90, 90, 2560))
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
result = MODULE_FUNCTION(raster, **DEFAULT_ARGS)
|
|
162
|
+
graph = result.__dask_graph__()
|
|
163
|
+
task_count = len(graph)
|
|
164
|
+
tasks_per_chunk = task_count / 100.0
|
|
165
|
+
|
|
166
|
+
# Check for fan-out: any task key that depends on more than 4 other tasks
|
|
167
|
+
deps = dict(graph)
|
|
168
|
+
max_fan_in = 0
|
|
169
|
+
for key, val in deps.items():
|
|
170
|
+
if hasattr(val, '__dask_graph__'):
|
|
171
|
+
sub = val.__dask_graph__()
|
|
172
|
+
max_fan_in = max(max_fan_in, len(sub))
|
|
173
|
+
|
|
174
|
+
print(json.dumps({
|
|
175
|
+
"success": True,
|
|
176
|
+
"task_count": task_count,
|
|
177
|
+
"tasks_per_chunk": round(tasks_per_chunk, 2),
|
|
178
|
+
"max_fan_in": max_fan_in,
|
|
179
|
+
"extrapolation_30tb": "~{} tasks at 57M chunks".format(
|
|
180
|
+
int(tasks_per_chunk * 57_000_000))
|
|
181
|
+
}))
|
|
182
|
+
except Exception as e:
|
|
183
|
+
print(json.dumps({"success": False, "error": str(e)}))
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Adapt the function call and imports for the specific module. Run the script
|
|
187
|
+
and capture its JSON output. If it errors, record the error and rely on
|
|
188
|
+
Part A alone.
|
|
189
|
+
|
|
190
|
+
**Verdict:** One of:
|
|
191
|
+
- `SAFE` — memory bounded by chunk size, graph scales linearly
|
|
192
|
+
- `RISKY` — bounded but tight (e.g. large overlap depth, 3D intermediates)
|
|
193
|
+
- `WILL OOM` — forces full materialization or unbounded memory growth
|
|
194
|
+
|
|
195
|
+
### 3. GPU Transfer Analysis
|
|
196
|
+
|
|
197
|
+
Scan for CuPy/CUDA code paths. Flag:
|
|
198
|
+
|
|
199
|
+
- HIGH: `.data.get()` followed by CuPy operations (GPU-CPU-GPU round-trip)
|
|
200
|
+
- HIGH: `cupy.asarray()` inside a loop (repeated CPU-GPU transfers)
|
|
201
|
+
- MEDIUM: Mixing NumPy and CuPy ops in same function without clear reason
|
|
202
|
+
- MEDIUM: Register pressure — count float64 local variables in `@cuda.jit`
|
|
203
|
+
kernels; flag if >20
|
|
204
|
+
- MEDIUM: Thread blocks >16x16 on kernels with >20 float64 locals
|
|
205
|
+
|
|
206
|
+
If the module has NO GPU code path, note "no GPU backend" and skip.
|
|
207
|
+
|
|
208
|
+
### 4. Memory Allocation Patterns
|
|
209
|
+
|
|
210
|
+
- MEDIUM: Unnecessary `.copy()` on arrays never mutated downstream
|
|
211
|
+
- MEDIUM: Large temporary arrays that could be fused into the kernel
|
|
212
|
+
- LOW: `np.zeros_like()` + fill loop where `np.empty()` would suffice
|
|
213
|
+
|
|
214
|
+
### 5. Numba Anti-Patterns
|
|
215
|
+
|
|
216
|
+
- MEDIUM: Missing `@ngjit` on nested for-loops over `.data` arrays
|
|
217
|
+
- MEDIUM: `@jit` without `nopython=True` (object-mode fallback risk)
|
|
218
|
+
- LOW: Type instability — initializing with int then assigning float
|
|
219
|
+
- LOW: Column-major iteration on row-major arrays (inner loop should be last axis)
|
|
220
|
+
|
|
221
|
+
### 6. Bottleneck Classification
|
|
222
|
+
|
|
223
|
+
Based on your analysis, classify the module as ONE of:
|
|
224
|
+
- `IO-bound` — dominated by disk reads/writes or serialization
|
|
225
|
+
- `memory-bound` — peak allocation is the limiting factor
|
|
226
|
+
- `compute-bound` — CPU/GPU time dominates, memory is fine
|
|
227
|
+
- `graph-bound` — dask task graph overhead dominates
|
|
228
|
+
|
|
229
|
+
### Output Format
|
|
230
|
+
|
|
231
|
+
Return EXACTLY this JSON structure (no extra text before or after):
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"module": "MODULE_NAME",
|
|
236
|
+
"files_read": ["list of files you read"],
|
|
237
|
+
"findings": [
|
|
238
|
+
{
|
|
239
|
+
"severity": "HIGH|MEDIUM|LOW",
|
|
240
|
+
"category": "dask_materialization|dask_chunking|gpu_transfer|register_pressure|memory_allocation|numba_antipattern",
|
|
241
|
+
"file": "filename.py",
|
|
242
|
+
"line": 123,
|
|
243
|
+
"description": "what the issue is",
|
|
244
|
+
"fix": "how to fix it",
|
|
245
|
+
"backends_affected": ["dask+numpy", "dask+cupy", "cupy", "numpy"]
|
|
246
|
+
}
|
|
247
|
+
],
|
|
248
|
+
"oom_verdict": {
|
|
249
|
+
"dask_numpy": "SAFE|RISKY|WILL OOM",
|
|
250
|
+
"dask_cupy": "SAFE|RISKY|WILL OOM",
|
|
251
|
+
"reasoning": "one-sentence explanation",
|
|
252
|
+
"estimated_peak_per_chunk_mb": 0.5,
|
|
253
|
+
"task_count": 3721,
|
|
254
|
+
"tasks_per_chunk": 37.21,
|
|
255
|
+
"graph_simulation_ran": true
|
|
256
|
+
},
|
|
257
|
+
"bottleneck": "compute-bound|memory-bound|IO-bound|graph-bound",
|
|
258
|
+
"bottleneck_reasoning": "one-sentence explanation"
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
IMPORTANT: Only flag patterns that are ACTUALLY present in the code. Do not
|
|
263
|
+
report hypothetical issues. False positives are worse than missed issues.
|
|
264
|
+
If a pattern like `.values` is used on a known-numpy-only code path, do not
|
|
265
|
+
flag it.
|
|
266
|
+
~~~
|
|
267
|
+
|
|
268
|
+
Wait for all subagents to return before proceeding to Step 4.
|
|
269
|
+
|
|
270
|
+
## Step 4 -- Merge results and print the triage report
|
|
271
|
+
|
|
272
|
+
Parse the JSON returned by each subagent. If a subagent returned malformed
|
|
273
|
+
output, record the module as "audit failed" with a note.
|
|
274
|
+
|
|
275
|
+
### 4a. Print the Module Risk Ranking Table
|
|
276
|
+
|
|
277
|
+
Sort modules by score descending. Print:
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
## Performance Sweep — Static Triage Report
|
|
281
|
+
|
|
282
|
+
### Module Risk Ranking
|
|
283
|
+
| Rank | Module | Score | OOM Verdict | Bottleneck | HIGH | MED | LOW |
|
|
284
|
+
|------|-----------------|--------|-----------------|---------------|------|-----|-----|
|
|
285
|
+
| 1 | geotiff | 31200 | WILL OOM (d+np) | IO-bound | 3 | 1 | 0 |
|
|
286
|
+
| 2 | viewshed | 30050 | RISKY (d+np) | memory-bound | 2 | 2 | 1 |
|
|
287
|
+
| ... | ... | ... | ... | ... | ... | ... | ... |
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
If `--high-only` is set, only count HIGH findings and omit modules with zero HIGH.
|
|
291
|
+
|
|
292
|
+
### 4b. Print the 30TB / 16GB Verdict Summary
|
|
293
|
+
|
|
294
|
+
Group modules by OOM verdict:
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
### 30TB on Disk / 16GB RAM — Out-of-Memory Analysis
|
|
298
|
+
|
|
299
|
+
#### WILL OOM (fix required)
|
|
300
|
+
- **module_name**: reasoning from subagent
|
|
301
|
+
|
|
302
|
+
#### RISKY (bounded but tight)
|
|
303
|
+
- **module_name**: reasoning from subagent
|
|
304
|
+
|
|
305
|
+
#### SAFE (memory bounded by chunk size)
|
|
306
|
+
- module_name, module_name, module_name, ...
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 4c. Print Detailed Findings
|
|
310
|
+
|
|
311
|
+
For each module that has findings, print a severity-grouped table:
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
### module_name (bottleneck: compute-bound, OOM: SAFE)
|
|
315
|
+
|
|
316
|
+
| # | Severity | File:Line | Category | Description | Fix |
|
|
317
|
+
|---|----------|----------------|-------------------------|------------------------------|-------------------------------|
|
|
318
|
+
| 1 | HIGH | slope.py:142 | dask_materialization | .values on dask input | Use .data or stay lazy |
|
|
319
|
+
| 2 | MEDIUM | slope.py:88 | dask_chunking | map_overlap depth too large | Reduce depth or warn users |
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### 4d. Print Actionable Rockout Commands
|
|
323
|
+
|
|
324
|
+
For each HIGH-severity finding, print a ready-to-paste `/rockout` command:
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
### Ready-to-Run Fixes (HIGH severity only)
|
|
328
|
+
|
|
329
|
+
1. **geotiff** — eager .values materialization (WILL OOM)
|
|
330
|
+
/rockout "Fix eager .values materialization in geotiff reader.
|
|
331
|
+
The dask read path at reader.py:87 calls .values which forces
|
|
332
|
+
the full array into memory. For 30TB inputs this will OOM on
|
|
333
|
+
a 16GB machine. Must stay lazy through the entire read path."
|
|
334
|
+
|
|
335
|
+
2. **cost_distance** — iterative solver unbounded memory (WILL OOM)
|
|
336
|
+
/rockout "Fix cost_distance iterative solver to work within
|
|
337
|
+
bounded memory. Currently materializes the full distance matrix
|
|
338
|
+
each iteration. Must use chunked iteration for 30TB dask inputs."
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Construct each `/rockout` command from the finding's description and fix fields.
|
|
342
|
+
Include the OOM verdict and bottleneck classification in the prompt text so
|
|
343
|
+
rockout has full context.
|
|
344
|
+
|
|
345
|
+
## Step 5 -- Update state file
|
|
346
|
+
|
|
347
|
+
Write `.claude/performance-sweep-state.json` with the triage results:
|
|
348
|
+
|
|
349
|
+
```json
|
|
350
|
+
{
|
|
351
|
+
"last_triage": "<current ISO datetime>",
|
|
352
|
+
"modules": {
|
|
353
|
+
"<module_name>": {
|
|
354
|
+
"last_inspected": "<current ISO datetime>",
|
|
355
|
+
"oom_verdict": "<SAFE|RISKY|WILL OOM>",
|
|
356
|
+
"bottleneck": "<IO-bound|memory-bound|compute-bound|graph-bound>",
|
|
357
|
+
"high_count": "<number of HIGH findings>",
|
|
358
|
+
"issue": null
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
If the file already exists, merge — update entries for modules that were
|
|
365
|
+
just audited, keep entries for modules not in this run's scope.
|
|
366
|
+
|
|
367
|
+
If `--report-only` is set, stop here. Do not proceed to Step 6.
|
|
368
|
+
|
|
369
|
+
## Step 6 -- Generate the ralph-loop command
|
|
370
|
+
|
|
371
|
+
Collect all modules from Step 4 (or from the state file if `--skip-phase1`)
|
|
372
|
+
that have at least one HIGH-severity finding and no `issue` recorded in the
|
|
373
|
+
state file (i.e. not yet fixed).
|
|
374
|
+
|
|
375
|
+
Sort them by: WILL OOM first, then RISKY, then by HIGH count descending.
|
|
376
|
+
|
|
377
|
+
Determine the benchmark array size from arguments:
|
|
378
|
+
- `--size small` → 128x128
|
|
379
|
+
- `--size large` → 2048x2048
|
|
380
|
+
- default → 512x512
|
|
381
|
+
|
|
382
|
+
### 6a. Print the ranked target list
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
### Phase 2 Targets (HIGH severity, unfixed)
|
|
386
|
+
| # | Module | HIGH Count | OOM Verdict | Bottleneck |
|
|
387
|
+
|---|---------------|------------|-------------|--------------|
|
|
388
|
+
| 1 | geotiff | 3 | WILL OOM | IO-bound |
|
|
389
|
+
| 2 | cost_distance | 1 | WILL OOM | memory-bound |
|
|
390
|
+
| 3 | viewshed | 2 | RISKY | memory-bound |
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
If no modules qualify, print:
|
|
394
|
+
"No HIGH-severity findings to fix. Run `/sweep-performance` without
|
|
395
|
+
`--skip-phase1` to refresh the triage."
|
|
396
|
+
Then stop.
|
|
397
|
+
|
|
398
|
+
### 6b. Print the ralph-loop command
|
|
399
|
+
|
|
400
|
+
Using the target list, generate and print:
|
|
401
|
+
|
|
402
|
+
````
|
|
403
|
+
/ralph-loop "Performance sweep Phase 2: benchmark and fix HIGH-severity findings.
|
|
404
|
+
|
|
405
|
+
**Target modules in priority order:**
|
|
406
|
+
1. <module> (<N> HIGH findings, <OOM verdict>) -- <one-line summary of worst finding>
|
|
407
|
+
2. <module> ...
|
|
408
|
+
...
|
|
409
|
+
|
|
410
|
+
**For each module, in order:**
|
|
411
|
+
|
|
412
|
+
1. Write a benchmark script at /tmp/perf_sweep_bench_<module>.py that:
|
|
413
|
+
- Imports the module's public functions
|
|
414
|
+
- Creates a test array (<SIZE>x<SIZE>, float64)
|
|
415
|
+
- For EACH available backend (numpy, dask+numpy; cupy and dask+cupy only if available):
|
|
416
|
+
a. Wrap the array in the appropriate DataArray type
|
|
417
|
+
b. Measure wall time: timeit.repeat(number=1, repeat=3), take median
|
|
418
|
+
c. Measure Python memory: tracemalloc.start() / tracemalloc.get_traced_memory()[1] for peak
|
|
419
|
+
d. Measure process memory: resource.getrusage(RUSAGE_SELF).ru_maxrss before and after
|
|
420
|
+
e. For CuPy backends: cupy.get_default_memory_pool().used_bytes() before and after
|
|
421
|
+
- Print results as JSON to stdout
|
|
422
|
+
|
|
423
|
+
2. Run the benchmark script and capture results.
|
|
424
|
+
|
|
425
|
+
3. Confirm the HIGH finding from Phase 1:
|
|
426
|
+
- If the dask backend uses significantly more memory than expected for
|
|
427
|
+
the chunk size, or wall time shows a materialization stall: CONFIRMED.
|
|
428
|
+
- If the benchmark shows no anomaly: downgrade to MEDIUM in state file,
|
|
429
|
+
print 'False positive — skipping' and move to the next module.
|
|
430
|
+
|
|
431
|
+
4. If confirmed: run /rockout to fix the issue end-to-end (issue, worktree,
|
|
432
|
+
implementation, tests, docs). Include the benchmark numbers in the
|
|
433
|
+
issue body for context.
|
|
434
|
+
|
|
435
|
+
5. After rockout completes: rerun the same benchmark script. Print a
|
|
436
|
+
before/after comparison:
|
|
437
|
+
| Backend | Metric | Before | After | Ratio | Verdict |
|
|
438
|
+
|------------|-------------|--------|--------|-------|------------|
|
|
439
|
+
| numpy | wall_ms | 45.2 | 12.1 | 0.27x | IMPROVED |
|
|
440
|
+
| dask+numpy | peak_rss_mb | 892 | 34 | 0.04x | IMPROVED |
|
|
441
|
+
Thresholds: IMPROVED < 0.8x, REGRESSION > 1.2x, else UNCHANGED.
|
|
442
|
+
|
|
443
|
+
6. Update .claude/performance-sweep-state.json with the issue number.
|
|
444
|
+
|
|
445
|
+
7. Output <promise>ITERATION DONE</promise>
|
|
446
|
+
|
|
447
|
+
If all targets have been addressed or confirmed as false positives:
|
|
448
|
+
<promise>ALL PERFORMANCE ISSUES FIXED</promise>." --max-iterations <N+2> --completion-promise "ALL PERFORMANCE ISSUES FIXED"
|
|
449
|
+
````
|
|
450
|
+
|
|
451
|
+
Set `--max-iterations` to the number of target modules plus 2 (buffer for
|
|
452
|
+
retries).
|
|
453
|
+
|
|
454
|
+
### 6c. Print reminder text
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
Phase 1 triage complete. To proceed with fixes:
|
|
458
|
+
Copy the ralph-loop command above and paste it.
|
|
459
|
+
|
|
460
|
+
Other options:
|
|
461
|
+
Fix one manually: copy any /rockout command from the report above
|
|
462
|
+
Rerun triage only: /sweep-performance --report-only
|
|
463
|
+
Skip Phase 1: /sweep-performance --skip-phase1 (reuses last triage)
|
|
464
|
+
Reset all tracking: /sweep-performance --reset-state
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## General Rules
|
|
470
|
+
|
|
471
|
+
- Phase 1 subagents do NOT modify any source, test, or benchmark files.
|
|
472
|
+
Read-only analysis only.
|
|
473
|
+
- Phase 2 ralph-loop modifies code only through `/rockout`.
|
|
474
|
+
- Temporary benchmark scripts and graph simulation scripts go in `/tmp/`
|
|
475
|
+
with unique names including the module name (e.g. `/tmp/perf_sweep_bench_slope.py`,
|
|
476
|
+
`/tmp/perf_sweep_graph_slope.py`). Clean them up after capturing results.
|
|
477
|
+
- Only flag patterns that are ACTUALLY present in the code. Do not report
|
|
478
|
+
hypothetical issues or patterns that "could" occur.
|
|
479
|
+
- Include the exact file path and line number for every finding so the user
|
|
480
|
+
can navigate directly to the issue.
|
|
481
|
+
- False positives are worse than missed issues. If you are not confident a
|
|
482
|
+
pattern is actually harmful in context (e.g. `.values` used intentionally
|
|
483
|
+
on a known-numpy array), do not flag it.
|
|
484
|
+
- The 30TB simulation constructs the dask task graph only; it NEVER calls
|
|
485
|
+
`.compute()`.
|
|
486
|
+
- State file (`.claude/performance-sweep-state.json`) is gitignored by
|
|
487
|
+
convention — do not add it to git.
|
|
488
|
+
- If $ARGUMENTS is empty, use defaults: audit all modules, benchmark at
|
|
489
|
+
512x512, generate ralph-loop for HIGH items.
|
|
490
|
+
- For subpackage modules (geotiff, reproject), the subagent should read ALL
|
|
491
|
+
`.py` files in the subpackage directory, not just `__init__.py`.
|
|
492
|
+
- When generating `/rockout` commands, include the OOM verdict, bottleneck
|
|
493
|
+
classification, and affected backends in the prompt text so rockout has
|
|
494
|
+
full performance context.
|