Source code for watershed_workflow.meteorology
"""Manipulate DayMet data structures.
DayMet is downloaded in box mode based on watershed bounds, then it can be converted to
hdf5 files that models can read.
"""
from typing import Tuple
import logging
import numpy as np
import xarray as xr
import datetime
import watershed_workflow.data
[docs]
def allocatePrecipitation(precip: xr.DataArray, air_temp: xr.DataArray,
transition_temperature: float) -> Tuple[xr.DataArray, xr.DataArray]:
"""Allocates precipitation between rain and snow based on temperature.
Parameters
----------
precip : xr.DataArray
Total precipitation data.
air_temp : xr.DataArray
Air temperature data.
transition_temperature : float
Temperature threshold for rain/snow transition. If < 100, assumed
to be in Celsius; otherwise Kelvin.
Returns
-------
rain : xr.DataArray
Rain precipitation (when temp >= transition_temperature).
snow : xr.DataArray
Snow precipitation (when temp < transition_temperature).
"""
if transition_temperature < 100:
tt_K = transition_temperature + 273.15
else:
tt_K = transition_temperature
rain = xr.where(air_temp >= tt_K, precip, 0)
snow = xr.where(air_temp < tt_K, precip, 0)
return rain, snow
[docs]
def convertDayMetToATS(dat: xr.Dataset, transition_temperature: float = 0.) -> xr.Dataset:
"""Convert xarray.Dataset Daymet datasets to daily average data in standard form.
This:
- takes tmin and tmax to compute a mean
- splits rain and snow precip based on mean air temp relative to transition_temperature [C]
- standardizes units and names for ATS
Parameters
----------
dat : xr.Dataset
Input Daymet dataset with variables: tmin, tmax, prcp, srad, dayl, vp.
transition_temperature : float, optional
Temperature threshold for rain/snow split in Celsius. Default is 0.
Returns
-------
xr.Dataset
Dataset with ATS-compatible variable names and units.
"""
logging.info('Converting DayMet to ATS met input')
# make missing values (-9999) as NaNs to do math while propagating NaNs
for key in dat.keys():
dat[key].data[dat[key].data == -9999] = np.nan
# note that all of these can live in the same dataset since they
# share the same coordinates/times
dout = xr.Dataset(coords=dat.coords, attrs=dat.attrs.copy())
mean_air_temp_c = (dat['tmin'] + dat['tmax']) / 2.0
dout['air temperature [K]'] = 273.15 + mean_air_temp_c # K
precip_ms = dat['prcp'] / 1.e3 / 86400. # mm/day --> m/s
# note that shortwave radiation in daymet is averged over the unit daylength, not per unit day.
dout['incoming shortwave radiation [W m^-2]'] = dat['srad'] * dat['dayl'] / 86400 # Wm2
dout['vapor pressure air [Pa]'] = dat['vp'] # Pa
dout['precipitation rain [m s^-1]'], dout['precipitation snow [m SWE s^-1]'] = \
allocatePrecipitation(precip_ms, mean_air_temp_c, transition_temperature)
return dout
[docs]
def convertAORCToATS(dat: xr.Dataset,
transition_temperature: float = 0.,
resample_interval: int = 1,
remove_leap_day: bool = False) -> xr.Dataset:
"""Convert xarray.Dataset AORC datasets to standard ATS format output.
- computes specific humidity and surface pressure to vapor pressure
- computes total wind speed from component wind speeds
- converts precip units to m/s
- allocates precip to snow and rain based on transition temp
Parameters
----------
dat : xr.Dataset
Input including AORC raw data.
transition_temperature : float
Temperature to transition from snow to rain [C]. Default is 0 C.
n_hourly : int
Convert data from 1-hourly to n_hourly to reduce data needs.
Defaults to 24 hours (daily data).
remove_leap_day : bool
If True, removes day 366 any leap year (not Feb 30!). Deafult
is False.
Returns
-------
xr.Dataset
Dataset with ATS-standard names/units met forcing.
"""
logging.info('Converting AORC to ATS met input')
# note that all of these can live in the same dataset since they
# share the same coordinates/times
dout = xr.Dataset(coords=dat.coords, attrs=dat.attrs.copy())
dout['air temperature [K]'] = dat['TMP_2maboveground']
dout['incoming shortwave radiation [W m^-2]'] = dat['DSWRF_surface']
dout['incoming longwave radiation [W m^-2]'] = dat['DLWRF_surface']
dout['vapor pressure air [Pa]'] = dat['SPFH_2maboveground'] * dat['PRES_surface'] \
/ (0.622 + dat['SPFH_2maboveground'])
dout.attrs['wind speed reference height [m]'] = 10.
dout['wind speed [m s^-1]'] = np.sqrt(
np.pow(dat['UGRD_10maboveground'], 2) + np.pow(dat['VGRD_10maboveground'], 2))
# convert mm --> m, hour --> s to get m/s
dout['precipitation total [m s^-1]'] = dat['APCP_surface'] / 1000 / 3600
# allocate precip
dout['precipitation rain [m s^-1]'], dout['precipitation snow [m SWE s^-1]'] = \
allocatePrecipitation(dout['precipitation total [m s^-1]'],
dout['air temperature [K]'], transition_temperature)
# convert times to standard time convention and remove leap day
dout['time'] = watershed_workflow.data.convertTimesToCFTime(dout['time'].values)
if remove_leap_day:
dout = watershed_workflow.data.filterLeapDay_DataFrame(dout)
return dout