Fix pycnophylactic crash on all-NaN zones or unmatched zone ids (#3406)#3586
Merged
Conversation
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
commented
Jul 1, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
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
validis computed and before the loop, sonp.nanmaxnever sees the empty slice.not valid.any()means every pixel is NaN, so returningsurfacehands back an all-NaN raster, which is whatdisaggregatedoes 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 raisesNotImplementedErrorand 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
commented
Jul 1, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
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).
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.
Summary
pycnophylacticraised an opaque numpyValueError("zero-size array to reduction operation fmax which has no identity") when no pixel was valid for smoothing. Two reachable cases: thezonesraster is entirely NaN, or none of the zone ids match thevaluesmapping. In both, thevalidmask is all-False and the convergence check runsnp.nanmaxover an empty slice.pycnophylacticbehaves likedisaggregateon the same inputs instead of crashing.xfail(strict)markers that were pinning the crash, and added a cupy-fallback case.Closes #3406
Backend coverage
pycnophylacticsupports numpy and cupy (CPU fallback); dask raisesNotImplementedErrorby 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)test_dasymetric.py: 78 passed