Fill all-NaN templates in generate_terrain (numpy/cupy)#3526
Merged
Conversation
The numpy and cupy backends zeroed their work buffer with data * 0, but NaN * 0 == NaN, so an all-NaN template (what from_template() returns) was never cleared and the octave loop accumulated noise onto NaN, leaving the whole output NaN. Saved GeoTIFFs read as 100% nodata in QGIS. Use np.zeros_like / cupy.zeros_like instead. The dask and dask+cupy paths already allocated fresh zeros per chunk and were unaffected. Adds tests covering all four backends: an all-NaN template now yields finite terrain identical to an all-zeros template.
brendancol
commented
Jun 25, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
PR Review: Fill all-NaN templates in generate_terrain (numpy/cupy)
Blockers (must fix before merge)
None.
Suggestions (should fix, not blocking)
- The dask and dask+cupy NaN-template tests (test_terrain.py:498, 513) only assert the result is finite, not that it matches the numpy result. Those backends were never broken and this PR doesn't touch them, so the gap is minor, but a
np.testing.assert_allclose(t_np, t_dask)would turn the all-NaN case into a real cross-backend parity check instead of a smoke test.
Nits (optional improvements)
- The two added code comments (terrain.py:184, 443) use em dashes. Project convention leans away from them; commas read the same here.
What looks good
- Root cause is right and the fix is minimal.
NaN * 0 == NaNwas the actual bug, andzeros_likeclears it while staying identical to the old behavior for any finite input, so there's no regression risk on normal templates. - Leaving the dask and dask+cupy paths alone is correct; they already allocate fresh zeros per chunk.
test_terrain_all_nan_template_matches_zeros_templateis a strong invariant. It pins the contract that generate_terrain ignores input values, and it would have failed on the old code.- All four backends are covered, and the GPU tests run on the CUDA dev box.
Checklist
- Algorithm matches reference: n/a (noise generation unchanged)
- All implemented backends produce consistent results: yes (numpy/cupy parity tested)
- NaN handling correct: yes (this is the fix)
- Edge cases covered: yes (all-NaN template, the reported case)
- Dask chunk boundaries: unchanged
- No premature materialization or unnecessary copies: yes
- Benchmark: not needed (bug fix, no perf change)
- README feature matrix: n/a (no API or backend-support change)
- Docstrings present and accurate: yes (behavior now matches the existing docstring)
…3525) - dask / dask+cupy NaN-template tests now assert allclose against the numpy result instead of only checking finiteness. - Replace em dashes in the two new code comments with parentheses.
brendancol
commented
Jun 25, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
Follow-up review (after ac34ba9)
Both findings from the first pass are addressed:
- Suggestion (cross-backend parity): the dask and dask+cupy NaN-template tests now run the same template through numpy and assert
np.testing.assert_allcloseagainst it, so the all-NaN case is a real parity check, not a smoke test. Renamed to..._dask_matches_numpy/..._dask_cupy_matches_numpy. - Nit (em dashes): the two new code comments now use parentheses.
No new findings. Full terrain suite passes locally (49 passed, including GPU and dask+cupy on the CUDA box). No blockers.
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 #3525
generate_terrain()returned all-NaN on the numpy and cupy backends whenthe template was all-NaN. That is exactly what
from_template()hands you,so the obvious pipeline (
from_template(...).xrs.generate_terrain().xrs.to_geotiff(...))wrote a GeoTIFF that QGIS reads as 100% nodata.
data * 0, butNaN * 0 == NaN, so the template's NaN values survived and the octaveloop accumulated noise onto NaN. Switched to
np.zeros_like/cupy.zeros_like.they were never affected and are unchanged.
Backend coverage: fix touches numpy + cupy; dask and dask+cupy already
worked. New tests run against all four.
Test plan:
pytest xrspatial/tests/test_terrain.py(49 passed locally, incl. GPU)