Aim sky_view_factor rays in ground azimuth space (#3626)#3634
Open
brendancol wants to merge 2 commits into
Open
Aim sky_view_factor rays in ground azimuth space (#3626)#3634brendancol wants to merge 2 commits into
brendancol wants to merge 2 commits into
Conversation
Ray directions were uniform in cell-index angle, so rectangular cells (cellsize_x != cellsize_y) sampled ground azimuths non-uniformly and biased the SVF average. Convert each ground azimuth to a unit step in cell-index space instead. Square-cell results are unchanged. Also records the 2026-07-03 accuracy sweep state for the module.
brendancol
commented
Jul 3, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
PR Review: Aim sky_view_factor rays in ground azimuth space (#3626)
Blockers (must fix before merge)
None.
Suggestions (should fix, not blocking)
-
xrspatial/sky_view_factor.py:159-166(and the GPU twin at 213-220): the direction table (dx,dyper azimuth) is recomputed for every pixel. That was true before this PR too, and the inner radius loop dominates runtime, so this is not a merge concern. If the kernels get touched again, precomputing then_directionsunit vectors once per array would drop two divides and a sqrt per pixel-direction.
Nits (optional improvements)
-
xrspatial/sky_view_factor.py:326-329: the new docstring text explains the behavior well. A sentence noting the residual limit would round it out: at extreme aspect ratios (say 10:1) the integer-cell ray marching still quantizes the sampled azimuths, so the evenly spaced azimuths are nominal, met to within one cell of rounding. The new consistency test only covers 2:1, which is the realistic range. - No asv benchmark exists for
sky_view_factor(pre-existing gap, and this PR adds no new function). Flagging for the benchmark sweep rather than for this PR.
What looks good
- The fix is minimal and mathematically right: dividing the ground-azimuth unit vector by cell size and renormalizing gives a unit cell-index step, so
map_overlapdepth semantics (depth == max_radius) are untouched. - Square-cell output is bit-identical to main (verified on a 40x40 random grid), so no existing user results change.
test_anisotropic_cellsize_ground_truth_consistencyfails on main (diffs 0.046/0.035 vs the 0.02 tolerance) and passes here with 3x headroom (0.0065/0.0061). It encodes a physical invariant (grid aspect cannot change the SVF of the same terrain), not a snapshot value.- The changed CUDA path gets its own parity tests (
test_anisotropic_numpy_equals_dask,test_anisotropic_numpy_equals_cupy); the full suite ran on a CUDA host with cupy and dask+cupy executing, 38 passed. dnormcannot be zero: cos and sin cannot both vanish and the public wrapper floors non-positive cell sizes to 1.0 before the kernels run.
Checklist
- Algorithm matches reference/paper (Zakšek et al. 2011 formula unchanged; only azimuth spacing corrected)
- All implemented backends produce consistent results (CPU and CUDA kernels carry the same change; parity tests added)
- NaN handling is correct (untouched by this PR; center-NaN and ray-truncation semantics unchanged)
- Edge cases are covered by tests (flat surface, cell-size overrides, anisotropic aspect ratios)
- Dask chunk boundaries handled correctly (direction depends only on scalar cell sizes, identical per chunk; depth unchanged)
- No premature materialization or unnecessary copies
- Benchmark exists or is not needed (no sky_view_factor benchmark; pre-existing gap, see nit)
- README feature matrix updated (not applicable, no new function or backend change)
- Docstrings present and accurate
brendancol
commented
Jul 3, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
PR Review: follow-up after 66d805d
Disposition of the first review's findings:
- Suggestion (per-pixel direction table): fixed for the CPU kernel.
_svf_cpunow fillsdxs/dysonce before the pixel loops (xrspatial/sky_view_factor.py:148-159) and output is bit-identical to the previous commit on a 40x40 random grid. The CUDA kernel keeps per-thread computation:cuda.local.arrayneeds a compile-time size andn_directionsis a runtime argument, so a table there means a kernel signature change threaded through_run_cupyand_run_dask_cupy. Out of scope for an accuracy fix; per-thread trig is normal CUDA style anyway. - Nit (docstring): added. The public docstring now says azimuth spacing is nominal to within one cell of rounding and quantizes toward the grid axes beyond roughly 4:1 aspect (
xrspatial/sky_view_factor.py:326-332). - Nit (missing asv benchmark): not addressed here, routed to the benchmark sweep. Pre-existing gap, no new function in this PR.
Checks on the delta itself: the hoisted table is filled with the same expressions in the same order, np.empty inside @ngjit is fine, and the full test file passes (38 tests, CUDA host). No new findings.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3626
_svf_cpuand_svf_gpunow start each ray from a ground azimuth and convert it to a unit step in cell-index space (cos(phi)/cellsize_x,sin(phi)/cellsize_y, renormalized). With square cells this reduces to the old directions; a 40x40 random grid produces bit-identical output before and after.Backend coverage: the CPU and CUDA kernels carry the same change, so numpy, cupy, dask+numpy, and dask+cupy all pick it up. Verified on a CUDA host.
Test plan:
pytest xrspatial/tests/test_sky_view_factor.py-- 38 passed on a CUDA host (cupy and dask+cupy paths ran, not skipped)test_anisotropic_cellsize_ground_truth_consistencyfails on main (diffs 0.046 / 0.035 vs 0.02 tolerance), passes hereAlso carries the accuracy-sweep state CSV row for this module (2026-07-03 re-audit).