Accept m/min spread rate in fireline_intensity by default (#3527)#3529
Merged
Conversation
rate_of_spread returns m/min, but fireline_intensity applied Byram's
equation as if the rate were m/s, so chaining the two produced
intensities 60x too high. Add a spread_rate_units parameter ('m/min'
default to match rate_of_spread, or 'm/s') and convert to m/s before
applying Byram's equation. Update the docstrings, the example, and the
user-guide notebook to drop the manual /60 workaround.
brendancol
commented
Jun 25, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
PR Review: Accept m/min spread rate in fireline_intensity by default
Blockers (must fix before merge)
None.
Suggestions (should fix, not blocking)
- Backward-compat visibility: the new default flips existing callers who passed an m/s spread rate without naming the unit; their results now come out 60x smaller. The choice is intentional, but it is a silent behavior change, so it should be called out in the release notes when the changelog is updated at release time (the changelog is deliberately untouched here).
xrspatial/fire.py:489
Nits (optional improvements)
- The equation line in the docstring still reads
I = H * w * R...*R* is rate of spread (m/s)(xrspatial/fire.py:492-493). The paragraph right below resolves it (the equation wants m/s; the input defaults to m/min), but a reader skimming only the formula line could be briefly confused. Optional wording tweak. - The unit-conversion math is asserted directly only on the numpy backend (
test_default_units_are_m_per_min,test_m_per_min_is_sixty_times_m_per_s). Cross-backend coverage comes transitively: the existing numpy/cupy/dask parity tests now run with the m/min default, so the/60path is exercised on all four backends. Adequate as-is; noting for the record.
What looks good
- The conversion is a single scalar divide on
spread_rate_agg.databefore dispatch, so it stays lazy on dask and backend-agnostic across numpy/cupy/dask+cupy. No premature materialization. - NaN-safe: NaN/60 stays NaN and the kernel's
r != rguard still fires. spread_rate_unitsis validated with a clear ValueError; docstrings on both functions now state units and cross-reference each other.- The manual
/60workaround is removed from both the example and the user-guide notebook, so the docs now demonstrate the corrected chaining. - Full
test_fire.pypasses locally (77 tests, all four backends on this CUDA box).
Checklist
- Algorithm matches reference (Byram I = HwR needs R in m/s; /60 from m/min is correct)
- All implemented backends produce consistent results (parity tests pass under new default)
- NaN handling is correct
- Edge cases covered (NaN propagation, invalid-units ValueError, chaining)
- Dask chunk boundaries handled (scalar op preserves chunking)
- No premature materialization or unnecessary copies
- Benchmark not needed (no perf-critical change)
- README feature matrix not applicable (no new function, no backend change)
- Docstrings present and accurate
brendancol
commented
Jun 25, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
Follow-up review (after 9171d53)
The docstring nit from the first pass is addressed: the equation line now reads *R* is rate of spread in m/s (see spread_rate_units ...), so the formula no longer reads as contradicting the m/min input default (xrspatial/fire.py:492-494).
Re-ran test_fire.py::TestFirelineIntensity after the change: 11 passed.
Remaining items are non-code and intentional:
- Release-notes call-out of the default unit change: deferred to release time (the changelog is deliberately untouched during this workflow).
- Direct unit-conversion assertions live on the numpy backend; the other three backends cover the
/60path transitively through the existing parity tests, which now run under the m/min default. Adequate as-is.
No blockers. Recommend merge once CI is green.
# Conflicts: # xrspatial/fire.py
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 #3527
rate_of_spreadreturns spread rate in m/min, butfireline_intensityapplied Byram'sI = H*w*Ras if R were in m/s. Chaining them gave fireline intensities 60x too high. The example already worked around this by dividing the spread rate by 60 before the call; a user chaining directly got no warning.fireline_intensitynow takesspread_rate_units, defaulting to'm/min'so the output ofrate_of_spreadcan be passed straight in. m/min inputs are divided by 60 before Byram's equation; pass'm/s'for SI spread rates./60conversion inexamples/fire_propagation.pyand the user-guide notebook.Backend coverage: the unit conversion is a scalar operation on the spread DataArray's
.databefore dispatch, so it works the same on numpy, cupy, dask+numpy, and dask+cupy. The existing cross-backend parity tests are unaffected (the factor applies uniformly).Test plan: