def test_fire_weather_ufunc_overwintering(): ds = open_dataset("ERA5/daily_surface_cancities_1990-1993.nc") ds = ds.assign( rh=atmos.relative_humidity_from_dewpoint(ds=ds), ws=convert_units_to(atmos.wind_speed_from_vector(ds=ds)[0], "km/h"), tas=convert_units_to(ds.tas, "degC"), pr=convert_units_to(ds.pr, "mm/d"), ) season_mask_all = fire_season(ds.tas, method="WF93", temp_end_thresh="4 degC") season_mask_all_LA08 = fire_season(ds.tas, snd=ds.swe, method="LA08") season_mask_yr = fire_season(ds.tas, method="WF93", freq="YS") # Mask is computed correctly and parameters are passed # season not passed so computed on the fly out1 = fire_weather_ufunc( tas=ds.tas, pr=ds.pr, lat=ds.lat, season_method="WF93", overwintering=False, temp_end_thresh=4, indexes=["DC"], ) np.testing.assert_array_equal(out1["season_mask"], season_mask_all) out2 = fire_weather_ufunc( tas=ds.tas, pr=ds.pr, snd=ds.swe, lat=ds.lat, season_method="LA08", overwintering=True, indexes=["DC"], ) np.testing.assert_array_equal(out2["season_mask"], season_mask_all_LA08) # Overwintering # Get last season's DC (from previous comp) and mask Saskatoon and Victoria dc0 = out2["DC"].ffill("time").isel(time=-1).where( [True, True, True, False, False]) winter_pr = out2["winter_pr"] out3 = fire_weather_ufunc( tas=ds.tas, pr=ds.pr, lat=ds.lat, winter_pr=winter_pr, season_mask=season_mask_yr, dc0=dc0, overwintering=True, indexes=["DC"], ) np.testing.assert_allclose(out3["winter_pr"].isel(location=0), 261.27353647, rtol=1e-6) np.testing.assert_array_equal(out3["DC"].notnull(), season_mask_yr)
def test_fire_weather_ufunc_drystart(atmosds): # This test is very shallow only tests if it runs. ds = atmosds.assign( tas=convert_units_to(atmosds.tas, "degC"), pr=convert_units_to(atmosds.pr, "mm/d"), ) season_mask_yr = fire_season(ds.tas, method="WF93", freq="YS") out_ds = fire_weather_ufunc( tas=ds.tas, pr=ds.pr, hurs=ds.hurs, lat=ds.lat, season_mask=season_mask_yr, overwintering=False, dry_start="CFS", indexes=["DC", "DMC"], dmc_dry_factor=5, ) out_no = fire_weather_ufunc( tas=ds.tas, pr=ds.pr, hurs=ds.hurs, lat=ds.lat, season_mask=season_mask_yr, overwintering=False, dry_start=None, indexes=["DC", "DMC"], ) # I know season of 1992 is a "wet" start. xr.testing.assert_identical( out_ds["DC"].sel(location="Montréal", time="1992"), out_no["DC"].sel(location="Montréal", time="1992"), ) xr.testing.assert_identical( out_ds["DMC"].sel(location="Montréal", time="1992"), out_no["DMC"].sel(location="Montréal", time="1992"), )
def test_fire_weather_ufunc_errors(tas_series, pr_series, rh_series, ws_series): tas = tas_series(np.ones(100), start="2017-01-01") pr = pr_series(np.ones(100), start="2017-01-01") rh = rh_series(np.ones(100), start="2017-01-01") ws = ws_series(np.ones(100), start="2017-01-01") snd = xr.full_like(tas, 0) lat = xr.full_like(tas.isel(time=0), 45) DC0 = xr.full_like(tas.isel(time=0), np.nan) DMC0 = xr.full_like(tas.isel(time=0), np.nan) FFMC0 = xr.full_like(tas.isel(time=0), np.nan) # Test invalid combination with pytest.raises(TypeError): fire_weather_ufunc( tas=tas, pr=pr, rh=rh, ws=ws, lat=lat, dc0=DC0, indexes=["DC", "ISI"], ) # Test missing arguments with pytest.raises(TypeError): fire_weather_ufunc( tas=tas, pr=pr, dc0=DC0, indexes=["DC"], # lat=lat, ) with pytest.raises(TypeError): fire_weather_ufunc( tas=tas, pr=pr, lat=lat, dc0=DC0, indexes=["DC"], start_up_mode="snow_depth", ) # Test starting too early with pytest.raises(ValueError): fire_weather_ufunc( tas=tas, pr=pr, lat=lat, dc0=DC0, snd=snd, indexes=["DC"], start_date="2017-01-01", start_up_mode="snow_depth", ) # Test output is complete out = fire_weather_ufunc( tas=tas, pr=pr, lat=lat, dc0=DC0, indexes=["DC"], start_date="2017-03-03", ) assert len(out.keys()) == 1 out = fire_weather_ufunc( tas=tas, pr=pr, rh=rh, ws=ws, lat=lat, snd=snd, dc0=DC0, dmc0=DMC0, ffmc0=FFMC0, indexes=["DSR"], start_date="2017-01-01", ) assert len(out.keys()) == 7
def test_fire_weather_ufunc_errors(tas_series, pr_series, hurs_series, sfcWind_series): tas = tas_series(np.ones(100), start="2017-01-01") pr = pr_series(np.ones(100), start="2017-01-01") hurs = hurs_series(np.ones(100), start="2017-01-01") sfcWind = sfcWind_series(np.ones(100), start="2017-01-01") snd = xr.full_like(tas, 0) lat = xr.full_like(tas.isel(time=0), 45) DC0 = xr.full_like(tas.isel(time=0), np.nan) # noqa DMC0 = xr.full_like(tas.isel(time=0), np.nan) # noqa FFMC0 = xr.full_like(tas.isel(time=0), np.nan) # noqa # Test invalid combination with pytest.raises(TypeError): fire_weather_ufunc( tas=tas, pr=pr, hurs=hurs, lat=lat, dc0=DC0, indexes=["DC", "ISI"], ) # Test missing arguments with pytest.raises(TypeError): fire_weather_ufunc( tas=tas, pr=pr, dc0=DC0, indexes=["DC"], # lat=lat, ) with pytest.raises(TypeError): fire_weather_ufunc( tas=tas, pr=pr, lat=lat, dc0=DC0, indexes=["DC"], season_method="LA08", ) # Test output is complete + dask out = fire_weather_ufunc( tas=tas.chunk(), pr=pr.chunk(), lat=lat.chunk(), dc0=DC0, indexes=["DC"], ) assert len(out.keys()) == 1 out["DC"].load() out = fire_weather_ufunc( tas=tas, pr=pr, hurs=hurs, sfcWind=sfcWind, lat=lat, snd=snd, dc0=DC0, dmc0=DMC0, ffmc0=FFMC0, indexes=["DSR"], ) assert len(out.keys()) == 7
def drought_code( tas: xarray.DataArray, pr: xarray.DataArray, lat: xarray.DataArray, snd: xarray.DataArray = None, dc0: xarray.DataArray = None, start_date: str = None, start_up_mode: str = None, shut_down_mode: str = "snow_depth", **params, ): r"""The daily drought code (FWI component) The drought code is part of the Canadian Forest Fire Weather Index System. It is a numeric rating of the average moisture content of organic layers. Parameters ---------- tas : xarray.DataArray Noon temperature. pr : xarray.DataArray Rain fall in open over previous 24 hours, at noon. lat : xarray.DataArray Latitude coordinate snd : xarray.DataArray Noon snow depth. dc0 : xarray.DataArray Initial values of the drought code. start_date : str, datetime.datetime Date at which to start the computation, dc0/dmc0/ffcm0 should be given at the day before. start_up_mode : {None, "snow_depth"} How to compute start up. Mode "snow_depth" requires the additional "snd" array. See the FWI submodule doc for valid values. shut_down_mode : {"temperature", "snow_depth"} How to compute shut down. Mode "snow_depth" requires the additional "snd" array. See the FWI submodule doc for valid values. params : Any other keyword parameters as defined in `xclim.indices.fwi.fire_weather_ufunc`. Returns ------- Drought code [-] Notes ----- See https://cwfis.cfs.nrcan.gc.ca/background/dsm/fwi References ---------- Y. Wang, K.R. Anderson, and R.M. Suddaby, INFORMATION REPORT NOR-X-424, 2015. """ tas = convert_units_to(tas, "C") pr = convert_units_to(pr, "mm/day") if snd is not None: snd = convert_units_to(snd, "m") if dc0 is None: dc0 = xarray.full_like(tas.isel(time=0), np.nan) params["start_date"] = start_date params["start_up_mode"] = start_up_mode out = fwi.fire_weather_ufunc(tas=tas, pr=pr, lat=lat, dc0=dc0, snd=snd, indexes=["DC"], **params) return out["DC"]
def fire_weather_indexes( tas: xarray.DataArray, pr: xarray.DataArray, ws: xarray.DataArray, rh: xarray.DataArray, lat: xarray.DataArray, snd: xarray.DataArray = None, ffmc0: xarray.DataArray = None, dmc0: xarray.DataArray = None, dc0: xarray.DataArray = None, start_date: str = None, **params, ): r"""Return the six daily fire weather indexes. Computes the 6 fire weather indexes as defined by the Canadian Forest Service: the Drought Code, the Duff-Moisture Code, the Fine Fuel Moisture Code, the Initial Spread Index, the Build Up Index and the Fire Weather Index. Parameters ---------- tas : xarray.DataArray Noon temperature. pr : xarray.DataArray Rain fall in open over previous 24 hours, at noon. ws : xarray.DataArray Noon wind speed. rh : xarray.DataArray Noon relative humidity. lat : xarray.DataArray Latitude coordinate snd : xarray.DataArray Noon snow depth. ffmc0 : xarray.DataArray Initial values of the fine fuel moisture code. dmc0 : xarray.DataArray Initial values of the Duff moisture code. dc0 : xarray.DataArray Initial values of the drought code. start_date : str, datetime.datetime Date at which to start the computation, dc0/dmc0/ffcm0 should be given at the day before. params : Any other keyword parameters as defined in `xclim.indices.fwi.fire_weather_ufunc`. Returns ------- DC, DMC, FFMC, ISI, BUI, FWI Notes ----- See https://cwfis.cfs.nrcan.gc.ca/background/dsm/fwi References ---------- Y. Wang, K.R. Anderson, and R.M. Suddaby, INFORMATION REPORT NOR-X-424, 2015. """ tas = convert_units_to(tas, "C") pr = convert_units_to(pr, "mm/day") ws = convert_units_to(ws, "km/h") rh = convert_units_to(rh, "pct") if snd is not None: snd = convert_units_to(snd, "m") if dc0 is None: dc0 = xarray.full_like(tas.isel(time=0), np.nan) if dmc0 is None: dmc0 = xarray.full_like(tas.isel(time=0), np.nan) if ffmc0 is None: ffmc0 = xarray.full_like(tas.isel(time=0), np.nan) params["start_date"] = start_date out = fwi.fire_weather_ufunc( tas=tas, pr=pr, rh=rh, ws=ws, lat=lat, dc0=dc0, dmc0=dmc0, ffmc0=ffmc0, snd=snd, indices=["DC", "DMC", "FFMC", "ISI", "BUI", "FWI"], **params, ) return out["DC"], out["DMC"], out["FFMC"], out["ISI"], out["BUI"], out[ "FWI"]