Reject source grids that don't share a horizontal lattice (#547) - #548
Merged
Conversation
LeafLux surface irradiance and Fosberg dead fuel moisture each derive an output by combining two source grids cell-for-cell in a processing service (treevox, griddle). Both assumed the grids were already on the same horizontal lattice and passed the raw arrays straight through, so a mismatch (e.g. a 2 m LAD grid paired with a 30 m terrain grid) surfaced only as an async worker crash with an UNEXPECTED_FAILURE and no actionable message — this was blocking end-to-end validation of the Fosberg grid (#540). Add a shared `validate_grids_share_horizontal_lattice` in grids/utils that compares two grids' CRS (semantically), trailing (y, x) shape, and affine transform, returning 422 with resample guidance. A 3D grid may pair with a 2D grid; only the horizontal lattice must match. Apply it in the LeafLux and Fosberg routers before the output document is created or a task dispatched, and also scope both LeafLux source lookups to the domain (cross-domain source now 404s). Compose already enforced this inline; it now delegates to the shared validator (behavior-preserving — compose grids are always 2D). The processing services keep their pre-aligned-input contract; the mismatch is caught synchronously at the API boundary instead. Fixes #547
This was referenced Aug 26, 2026
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
Closes #547.
irradiance.surface.relative(LeafLux) andfuel_moisture.dead.1hr(Fosberg) each derive an output by combining two source grids cell-for-cell inside a processing service (treevox, griddle). Both endpoints assumed the two grids already shared a horizontal lattice and passed the raw arrays straight through — so a mismatch (the common case: a fine 2 m LAD grid paired with a coarse 30 m terrain grid) surfaced only as an async worker crash:The job failed with
UNEXPECTED_FAILUREand no actionable message. Confirmed againsttreevox-v2-prodlogs (multiple occurrences). This was blocking end-to-end validation of the Fosberg 1-hr dead fuel moisture grid (#540).Approach
Catch the mismatch synchronously at the API boundary with a clear 422, and keep the processing services' existing pre-aligned-input contract intact (no resampling pushed into treevox/griddle).
validate_grids_share_horizontal_latticeingrids/utils.py: compares two grids' CRS (vialib.crs.crs_equal), trailing(y, x)shape, and the six affine-transform coefficients (abs tol1e-9). A 3D grid may pair with a 2D grid — only the horizontal lattice must match. Returns 422 with resample remediation guidance._shape_rank), soshape[-2:]equals the full shape.Validation runs before the output document is written or a task dispatched, so a rejected request creates no grid and dispatches no worker.
Tests
validate_grids_share_horizontal_latticeunit tests (matching 3D/2D, equivalent CRS spellings, and CRS / shape / transform mismatches).Verified locally against a live API server: unit tests 28 passed; router tests (LeafLux + Fosberg + Compose) 86 passed;
ruff check/ruff format --checkclean.Contract / downstream notes (Beta)
silvxlabs/fastfuels-sdk-python#197is held pending this fix; the SDK/docs should note that terrain/irradiance must share the consumer grid's lattice (422 otherwise). OpenAPI descriptions updated here.