Skip to content

Commit 3352718

Browse files
committed
fire: declare float32 dtype on dask+numpy map_blocks (#3394)
The six dask+numpy map_blocks wrappers (_dnbr_dask, _rdnbr_dask, _fli_dask, _fl_dask, _ros_dask, _kbdi_dask) passed meta=np.array(()) with no dtype= argument, so the lazy dask DataArray reported float64 while the @ngjit kernels return float32. Pass dtype=np.float32 to match the kernels and the other three backends. _bsc_dask already set dtype=np.int8. Adds a parametrized regression test asserting the dask+numpy declared dtype matches the numpy backend for every fire function.
1 parent 0662457 commit 3352718

2 files changed

Lines changed: 72 additions & 6 deletions

File tree

xrspatial/fire.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ def _dnbr_gpu(pre_data, post_data, out):
166166

167167

168168
def _dnbr_dask(pre_data, post_data):
169-
return da.map_blocks(_dnbr_cpu, pre_data, post_data, meta=np.array(()),
169+
return da.map_blocks(_dnbr_cpu, pre_data, post_data,
170+
dtype=np.float32, meta=np.array(()),
170171
**_dask_task_name_kwargs('xrspatial.dnbr'))
171172

172173

@@ -259,7 +260,8 @@ def _rdnbr_gpu(dnbr_data, pre_data, out):
259260

260261

261262
def _rdnbr_dask(dnbr_data, pre_data):
262-
return da.map_blocks(_rdnbr_cpu, dnbr_data, pre_data, meta=np.array(()),
263+
return da.map_blocks(_rdnbr_cpu, dnbr_data, pre_data,
264+
dtype=np.float32, meta=np.array(()),
263265
**_dask_task_name_kwargs('xrspatial.rdnbr'))
264266

265267

@@ -471,7 +473,7 @@ def _fli_gpu(fuel_data, spread_data, heat_content, out):
471473

472474
def _fli_dask(fuel_data, spread_data, heat_content):
473475
return da.map_blocks(_fli_cpu, fuel_data, spread_data, heat_content,
474-
meta=np.array(()),
476+
dtype=np.float32, meta=np.array(()),
475477
**_dask_task_name_kwargs('xrspatial.fireline_intensity'))
476478

477479

@@ -571,7 +573,8 @@ def _fl_gpu(intensity_data, out):
571573

572574

573575
def _fl_dask(intensity_data):
574-
return da.map_blocks(_fl_cpu, intensity_data, meta=np.array(()),
576+
return da.map_blocks(_fl_cpu, intensity_data,
577+
dtype=np.float32, meta=np.array(()),
575578
**_dask_task_name_kwargs('xrspatial.flame_length'))
576579

577580

@@ -743,7 +746,7 @@ def _ros_dask(slope_data, wind_data, moisture_data,
743746
_ros_cpu, slope_data, wind_data, moisture_data,
744747
w_0, h, M_x, beta, rho_b, Gamma, eta_s, xi, epsilon,
745748
C_w, B_w, E_w,
746-
meta=np.array(()),
749+
dtype=np.float32, meta=np.array(()),
747750
**_dask_task_name_kwargs('xrspatial.rate_of_spread'),
748751
)
749752

@@ -919,7 +922,7 @@ def _kbdi_gpu(kbdi_prev_data, max_temp_data, precip_data, annual_precip, out):
919922
def _kbdi_dask(kbdi_prev_data, max_temp_data, precip_data, annual_precip):
920923
return da.map_blocks(
921924
_kbdi_cpu, kbdi_prev_data, max_temp_data, precip_data, annual_precip,
922-
meta=np.array(()),
925+
dtype=np.float32, meta=np.array(()),
923926
**_dask_task_name_kwargs('xrspatial.kbdi'),
924927
)
925928

xrspatial/tests/test_fire.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,3 +664,66 @@ def test_dataset_flame_length_accessor(self):
664664
})
665665
result = ds.xrs.flame_length()
666666
assert isinstance(result, xr.Dataset)
667+
668+
669+
# ---------------------------------------------------------------------------
670+
# Backend dtype parity (regression for #3394)
671+
# ---------------------------------------------------------------------------
672+
# Each entry builds the function's call for a given backend so the dask path's
673+
# declared dtype can be compared against the numpy path. The dask map_blocks
674+
# wrappers used to omit dtype=, defaulting the declared dtype to float64 while
675+
# the kernels actually return float32.
676+
def _dnbr_call(backend):
677+
a = create_test_raster(np.array([[0.5, 0.6]], dtype='f4'), backend)
678+
b = create_test_raster(np.array([[0.1, 0.3]], dtype='f4'), backend)
679+
return dnbr(a, b)
680+
681+
682+
def _rdnbr_call(backend):
683+
a = create_test_raster(np.array([[0.4, 0.3]], dtype='f4'), backend)
684+
b = create_test_raster(np.array([[500.0, 200.0]], dtype='f4'), backend)
685+
return rdnbr(a, b)
686+
687+
688+
def _bsc_call(backend):
689+
a = create_test_raster(np.array([[0.5, -0.3]], dtype='f4'), backend)
690+
return burn_severity_class(a)
691+
692+
693+
def _fli_call(backend):
694+
a = create_test_raster(np.array([[2.0, 0.5]], dtype='f4'), backend)
695+
b = create_test_raster(np.array([[0.1, 0.2]], dtype='f4'), backend)
696+
return fireline_intensity(a, b)
697+
698+
699+
def _fl_call(backend):
700+
a = create_test_raster(np.array([[100.0, 500.0]], dtype='f4'), backend)
701+
return flame_length(a)
702+
703+
704+
def _ros_call(backend):
705+
slope = create_test_raster(np.full((2, 2), 10.0, dtype='f4'), backend)
706+
wind = create_test_raster(np.full((2, 2), 10.0, dtype='f4'), backend)
707+
moist = create_test_raster(np.full((2, 2), 0.06, dtype='f4'), backend)
708+
return rate_of_spread(slope, wind, moist)
709+
710+
711+
def _kbdi_call(backend):
712+
prev = create_test_raster(np.full((2, 2), 100.0, dtype='f4'), backend)
713+
temp = create_test_raster(np.full((2, 2), 30.0, dtype='f4'), backend)
714+
precip = create_test_raster(np.zeros((2, 2), dtype='f4'), backend)
715+
return kbdi(prev, temp, precip, annual_precip=1500.0)
716+
717+
718+
@dask_array_available
719+
@pytest.mark.parametrize("call", [
720+
_dnbr_call, _rdnbr_call, _bsc_call, _fli_call, _fl_call, _ros_call,
721+
_kbdi_call,
722+
])
723+
def test_dask_numpy_dtype_matches_numpy(call):
724+
np_r = call('numpy')
725+
dk_r = call('dask+numpy')
726+
# declared dtype on the lazy dask array must match numpy backend
727+
assert dk_r.dtype == np_r.dtype
728+
# and must match what the graph actually computes
729+
assert dk_r.data.compute().dtype == np_r.dtype

0 commit comments

Comments
 (0)