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