Skip to content

Fix pycnophylactic crash on all-NaN zones or unmatched zone ids (#3406)#3586

Merged
brendancol merged 3 commits into
mainfrom
issue-3406
Jul 1, 2026
Merged

Fix pycnophylactic crash on all-NaN zones or unmatched zone ids (#3406)#3586
brendancol merged 3 commits into
mainfrom
issue-3406

Conversation

@brendancol

Copy link
Copy Markdown
Contributor

Summary

  • pycnophylactic raised an opaque numpy ValueError ("zero-size array to reduction operation fmax which has no identity") when no pixel was valid for smoothing. Two reachable cases: the zones raster is entirely NaN, or none of the zone ids match the values mapping. In both, the valid mask is all-False and the convergence check runs np.nanmax over an empty slice.
  • The guard returns the all-NaN surface before the smoothing loop, so pycnophylactic behaves like disaggregate on the same inputs instead of crashing.
  • Removed the xfail(strict) markers that were pinning the crash, and added a cupy-fallback case.

Closes #3406

Backend coverage

pycnophylactic supports numpy and cupy (CPU fallback); dask raises NotImplementedError by design. The fix is in _pycnophylactic_numpy, which both supported paths share.

Test plan

  • test_pycnophylactic_all_nan_zones (numpy)
  • test_pycnophylactic_no_matching_zone (numpy)
  • test_pycnophylactic_all_nan_zones_cupy (CUDA box)
  • Full test_dasymetric.py: 78 passed

pycnophylactic raised an opaque numpy ValueError ("zero-size array to
reduction operation fmax") when no pixel was valid for smoothing -- the
zones raster was entirely NaN, or no zone id matched the values mapping.
The convergence check called np.nanmax on the empty valid slice.

Guard the empty-valid case before the smoothing loop and return the
all-NaN surface, matching disaggregate's behaviour on the same inputs.
Unpin the xfail(strict) coverage and add a cupy-fallback case.

@brendancol brendancol left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: Fix pycnophylactic crash on all-NaN zones or unmatched zone ids (#3406)

Blockers (must fix before merge)

None.

Suggestions (should fix, not blocking)

None.

Nits (optional improvements)

  • xrspatial/tests/test_dasymetric.py:1073 -- the class docstring's first line still reads "pycnophylactic crashes when no pixel is valid for smoothing", which is stale now that the fix removes the crash. The rest of the docstring already describes the fixed behaviour, so only the opening clause is out of date.

What looks good

  • The guard sits right after valid is computed and before the loop, so np.nanmax never sees the empty slice. not valid.any() means every pixel is NaN, so returning surface hands back an all-NaN raster, which is what disaggregate does on the same inputs.
  • Shape, dtype, coords and attrs match the normal return path.
  • Coverage hits both reachable cases (all-NaN zones, no matching zone id) on numpy, plus the cupy fallback that routes through the same _pycnophylactic_numpy. dask already raises NotImplementedError and is covered elsewhere.

Checklist

  • Behaviour now matches disaggregate
  • Implemented backends consistent (numpy + cupy fallback)
  • NaN handling correct
  • Edge cases covered by tests
  • Dask path unsupported, raises (tested elsewhere)
  • No premature materialization or unnecessary copies
  • Benchmark not needed (bug fix)
  • README feature matrix not applicable
  • Docstrings accurate (see stale class-docstring nit)

@brendancol brendancol left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up review (after 3e94d09)

The one nit from the previous pass is resolved: the TestPycnophylacticEmptyValid class docstring no longer claims pycnophylactic "crashes", so it now matches the fixed behaviour.

No new findings. No open Blockers, Suggestions, or Nits. Tests green locally (4 passed in the class, 78 in the full dasymetric suite).

@github-actions github-actions Bot added the performance PR touches performance-sensitive code label Jul 1, 2026
@brendancol brendancol merged commit 14a181f into main Jul 1, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pycnophylactic raises opaque ValueError on all-NaN zones or no matching zone id

1 participant