Exemplo n.º 1
0
def vapor_pressure(tdew):
    """
    Computes vapor pressure
    Note this should be jointly computed iteratively with
    ``pet``, ``tdew``, and ``shortwave`` as used
    in the main ``run`` function.

    Parameters
    ----------
    tdew:
        Daily dewpoint temperature

    Returns
    -------
    vapor_pressure:
        Estimated vapor pressure
    """
    return svp(tdew)
Exemplo n.º 2
0
def relative_humidity(vapor_pressure: pd.Series, temp: pd.Series):
    """
    Calculate relative humidity from vapor pressure
    and temperature.

    Parameters
    ----------
    vapor_pressure:
        A sub-daily timeseries of vapor pressure
    temp:
        A sub-daily timeseries of temperature

    Returns
    -------
    rh:
        A sub-daily timeseries of relative humidity
    """
    rh = cnst.MAX_PERCENT * cnst.MBAR_PER_BAR * (vapor_pressure / svp(temp))
    return rh.where(rh < cnst.MAX_PERCENT, cnst.MAX_PERCENT)
Exemplo n.º 3
0
def calc_srad_hum(df: pd.DataFrame, sg: dict, elev: float, params: dict):
    """
    Calculate shortwave, humidity

    Parameters
    ----------
    df:
        Dataframe containing daily timeseries
    elev:
        Elevation in meters
    params:
        A dictionary of parameters from the
        MetSim object
    """
    def _calc_tfmax(prec, dtr, sm_dtr):
        """Estimate cloudy day transmittance"""
        b = cnst.B0 + cnst.B1 * np.exp(-cnst.B2 * sm_dtr)
        t_fmax = 1.0 - 0.9 * np.exp(-b * np.power(dtr, cnst.C))
        inds = np.array(prec > params['sw_prec_thresh'])
        t_fmax[inds] *= params['rain_scalar']
        return t_fmax

    # Calculate the diurnal temperature range
    df['t_max'] = np.maximum(df['t_max'], df['t_min'])
    dtr = df['t_max'] - df['t_min']
    df['dtr'] = dtr
    sm_dtr = df['smoothed_dtr']
    df['tfmax'] = _calc_tfmax(df['prec'], dtr, sm_dtr)
    tdew = df.get('tdew', df['t_min'])
    pva = df.get('hum', svp(tdew.values))
    pa = atm_pres(elev, params['lapse_rate'])
    yday = df.index.dayofyear - 1
    df['dayl'] = sg['daylength'][yday]

    # Calculation of tdew and shortwave. tdew is iterated on until
    # it converges sufficiently
    tdew_old = tdew
    tdew, pva = sw_hum_iter(df, sg, pa, pva, dtr, params)
    while (np.sqrt(np.mean((tdew - tdew_old)**2)) > params['tdew_tol']):
        tdew_old = np.copy(tdew)
        tdew, pva = sw_hum_iter(df, sg, pa, pva, dtr, params)
    df['vapor_pressure'] = pva
Exemplo n.º 4
0
def relative_humidity(vapor_pressure: np.array, temp: np.array) -> np.array:
    """
    Calculate relative humidity from vapor pressure
    and temperature.

    Parameters
    ----------
    vapor_pressure:
        A sub-daily timeseries of vapor pressure
    temp:
        A sub-daily timeseries of temperature

    Returns
    -------
    rh:
        A sub-daily timeseries of relative humidity
    """
    rh = (cnst.MAX_PERCENT * cnst.MBAR_PER_BAR * (vapor_pressure / svp(temp)))
    rh[rh > cnst.MAX_PERCENT] = cnst.MAX_PERCENT
    return rh
Exemplo n.º 5
0
def vapor_pressure(vp_daily: np.array, temp: np.array, t_t_min: np.array,
                   n_out: int, ts: int) -> np.array:
    """
    Calculate vapor pressure.  First a linear interpolation
    of the daily values is calculated.  Then this is compared
    to the saturated vapor pressure calculated using the
    disaggregated temperature. When the interpolated vapor
    pressure is greater than the calculated saturated
    vapor pressure, the interpolation is replaced with the
    saturation value.

    Parameters
    ----------
    vp_daily:
        Daily vapor pressure
    temp:
        Sub-daily temperature
    t_t_min:
        Timeseries of minimum daily temperature
    n_out:
        Number of output observations
    ts:
        Timestep to disaggregate down to

    Returns
    -------
    vp:
        A sub-daily timeseries of the vapor pressure
    """
    # Linearly interpolate the values
    interp = scipy.interpolate.interp1d(t_t_min,
                                        vp_daily / cnst.MBAR_PER_BAR,
                                        bounds_error=False)
    vp_disagg = interp(ts * np.arange(0, n_out))

    # Account for situations where vapor pressure is higher than
    # saturation point
    vp_sat = svp(temp) / cnst.MBAR_PER_BAR
    vp_disagg = np.where(vp_sat < vp_disagg, vp_sat, vp_disagg)
    return vp_disagg
Exemplo n.º 6
0
def sw_hum_iter(df: pd.DataFrame, sg: dict, pa: float, pva: pd.Series,
                dtr: pd.Series, params: dict):
    """
    Calculated updated values for dewpoint temperature
    and saturation vapor pressure.

    Parameters
    ----------
    df:
        Dataframe containing daily timeseries of
        cloud cover fraction, tfmax, swe, and
        shortwave radiation
    sg:
        Solar geometry dictionary, calculated with
        `metsim.physics.solar_geom`.
    pa:
        Air pressure in Pascals
    pva:
        Vapor presure in Pascals
    dtr:
        Daily temperature range
    params:
        A dictionary of parameters from a MetSim object

    Returns
    -------
    (tdew, svp):
        A tuple of dewpoint temperature and saturation
        vapor pressure
    """
    tt_max0 = sg['tt_max0']
    potrad = sg['potrad']
    daylength = sg['daylength']
    yday = df.index.dayofyear - 1

    t_tmax = np.maximum(tt_max0[yday] + (cnst.ABASE * pva), 0.0001)
    t_final = t_tmax * df['tfmax']
    df['pva'] = pva
    df['tfinal'] = t_final

    # Snowpack contribution
    sc = np.zeros_like(df['swe'])
    if (params['mtclim_swe_corr']):
        inds = np.logical_and(df['swe'] > 0., daylength[yday] > 0.)
        sc[inds] = ((1.32 + 0.096 * df['swe'][inds]) * 1.0e6 /
                    daylength[yday][inds])
        sc = np.maximum(sc, 100.)

    # Calculation of shortwave is split into 2 components:
    # 1. Radiation from incident light
    # 2. Influence of snowpack - optionally set by MTCLIM_SWE_CORR
    df['shortwave'] = potrad[yday] * t_final + sc

    # Calculate cloud effect
    if (params['lw_cloud'].upper() == 'CLOUD_DEARDORFF'):
        df['tskc'] = (1. - df['tfmax'])
    else:
        df['tskc'] = np.sqrt((1. - df['tfmax']) / 0.65)

    # Compute PET using SW radiation estimate, and update Tdew, pva **
    pet = calc_pet(df['shortwave'].values, df['t_day'].values,
                   df['dayl'].values, pa)
    # Calculate ratio (PET/effann_prcp) and correct the dewpoint
    parray = df['seasonal_prec'] / cnst.MM_PER_CM
    ratio = pet / parray.where(parray > 8.0, 8.0)
    df['pet'] = pet * cnst.MM_PER_CM
    tmink = df['t_min'] + cnst.KELVIN
    tdew = tmink * (-0.127 + 1.121 *
                    (1.003 - 1.444 * ratio + 12.312 * np.power(ratio, 2) -
                     32.766 * np.power(ratio, 3)) + 0.0006 * dtr) - cnst.KELVIN
    return tdew, svp(tdew.values)
Exemplo n.º 7
0
def calc_srad_hum(df: pd.DataFrame,
                  sg: dict,
                  elev: float,
                  params: dict,
                  win_type: str = 'boxcar'):
    """
    Calculate shortwave, humidity

    Parameters
    ----------
    df:
        Dataframe containing daily timeseries
    elev:
        Elevation in meters
    params:
        A dictionary of parameters from the
        MetSim object
    win_type:
        (Optional) The method used to calculate
        the 60 day rolling average of precipitation
    """
    def _calc_tfmax(prec, dtr, sm_dtr):
        b = cnst.B0 + cnst.B1 * np.exp(-cnst.B2 * sm_dtr)
        t_fmax = 1.0 - 0.9 * np.exp(-b * np.power(dtr, cnst.C))
        inds = np.array(prec > params['sw_prec_thresh'])
        t_fmax[inds] *= cnst.RAIN_SCALAR
        return t_fmax

    # Calculate the diurnal temperature range
    df['t_max'] = np.maximum(df['t_max'], df['t_min'])
    dtr = df['t_max'] - df['t_min']
    sm_dtr = pd.Series(dtr).rolling(window=30, win_type=win_type,
                                    axis=0).mean().fillna(method='bfill')
    if params['n_days'] <= 30:
        warn('Timeseries is shorter than rolling mean window, filling ')
        warn('missing values with unsmoothed data')
        sm_dtr.fillna(dtr, inplace=True)

    # Effective annual prec
    if params['n_days'] <= 90:
        # Simple scaled method, minimum of 8 cm
        sum_prec = df['prec'].values.sum()
        eff_ann_prec = (sum_prec / params['n_days']) * cnst.DAYS_PER_YEAR
        eff_ann_prec = np.maximum(eff_ann_prec, 8.0)
        parray = pd.Series(eff_ann_prec, index=df.index)
    else:
        # Calculate effective annual prec using 3 month moving window
        window = pd.Series(np.zeros(params['n_days'] + 90))
        window[90:] = df['prec']

        # If yeardays at end match with those at beginning we can use
        # the end of the input to generate the beginning by looping around
        # If not, just duplicate the first 90 days
        start_day, end_day = df.index.dayofyear[0], df.index.dayofyear[-1]
        if ((start_day % 365 == (end_day % 365) + 1)
                or (start_day % 366 == (end_day % 366) + 1)):
            window[:90] = df['prec'][-90:]
        else:
            window[:90] = df['prec'][:90]

        parray = cnst.DAYS_PER_YEAR * window.rolling(
            window=90, win_type=win_type, axis=0).mean()[90:]

    # Convert to cm
    parray = parray.where(parray > 80.0, 80.0) / cnst.MM_PER_CM
    # Doing this way because parray.reindex_like(df) returns all nan
    parray.index = df.index
    df['tfmax'] = _calc_tfmax(df['prec'], dtr, sm_dtr)
    tdew = df.get('tdew', df['t_min'])
    pva = df.get('hum', svp(tdew))
    pa = atm_pres(elev)
    yday = df.index.dayofyear - 1
    df['dayl'] = sg['daylength'][yday]

    # Calculation of tdew and swrad. tdew is iterated on until
    # it converges sufficiently
    tdew_old = tdew
    tdew, pva = sw_hum_iter(df, sg, pa, pva, parray, dtr, params)
    while (np.sqrt(np.mean((tdew - tdew_old)**2)) > params['tdew_tol']):
        tdew_old = np.copy(tdew)
        tdew, pva = sw_hum_iter(df, sg, pa, pva, parray, dtr, params)
    df['vapor_pressure'] = pva
Exemplo n.º 8
0
def calc_srad_hum_it(df, tol=0.01, win_type='boxcar'):
    """
    TODO
    """
    window = np.zeros(n_days + 90)
    t_fmax = np.zeros(n_days)
    df['s_tfmax'] = 0.0

    df['t_max'] = np.maximum(df['t_max'], df['t_min'])
    dtr = df['t_max'] - df['t_min']
    sm_dtr = pd.rolling_window(dtr, window=30, freq='D',
                               win_type=win_type).fillna(method='bfill')
    if n_days <= 30:
        print('Timeseries is shorter than rolling mean window, filling ')
        print('missing values with unsmoothed data')
        sm_dtr.fillna(dtr, inplace=True)

    sum_precip = df['s_precip'].values.sum()
    ann_precip = (sum_precip / n_days) * consts['DAYS_PER_YEAR']
    if ann_precip == 0.0:
        ann_precip = 1.0

    if n_days <= 90:
        sum_precip = df['s_precip'].values.sum()
        eff_ann_precip = (sum_precip / n_days) * consts['DAYS_PER_YEAR']
        eff_ann_precip = np.maximum(eff_ann_precip, 8.0)
        parray = eff_ann_precip
    else:
        parray = np.zeros(n_days)
        start_yday = df['day_of_year'][0]
        end_yday = df['day_of_year'][-1]
        if start_yday != 1:
            if end_yday == start_yday - 1:
                isloop = True
        else:
            if end_yday == 365 or end_yday == 366:
                isloop = True

        if isloop:
            for i in range(90):
                window[i] = df['s_precip'][n_days - 90 + i]
        else:
            for i in range(90):
                window[i] = df['s_precip'][i]
        window[90:] = df['s_precip']

        for i in range(n_days):
            sum_precip = 0.0
            for j in range(90):
                sum_precip += window[i + j]
                sum_precip = (sum_precip / 90.) * consts['DAYS_PER_YEAR']
            sum_precip = np.maximum(sum_precip, 8.0)
            parray[i] = sum_precip

    # FIXME: This is still bad form
    tt_max0, flat_potrad, slope_potrad, daylength, tiny_rad_fract = calc_solar_geom(
    )
    # NOTE: Be careful with this one!
    disaggregate.tiny_rad_fract = tiny_rad_fract

    avg_horizon = (params['site_east_horiz'] + params['site_west_horiz']) / 2.0
    horizon_scalar = 1.0 - np.sin(avg_horizon * consts['RADPERDEG'])
    if (params['site_slope'] > avg_horizon):
        slope_excess = params['site_slope'] - avg_horizon
    else:
        slope_excess = 0.
    if (2.0 * avg_horizon > 180.):
        slope_scalar = 0.
    else:
        slope_scalar = np.clip(
            1. - (slope_excess / (180.0 - 2.0 * avg_horizon)), 0, None)
    sky_prop = horizon_scalar * slope_scalar
    b = params['B0'] + params['B1'] * np.exp(-params['B2'] * sm_dtr)
    t_fmax = 1.0 - 0.9 * np.exp(-b * np.power(dtr, params['C']))
    inds = np.nonzero(df['precip'] > options['SW_PREC_THRESH'])[0]
    t_fmax[inds] *= params['RAIN_SCALAR']
    df['s_tfmax'] = t_fmax

    tdew = df.get('tdew', df['s_t_min'])
    pva = df['s_hum'] if 's_hum' in df else svp(tdew)

    pa = atm_pres(params['site_elev'])
    yday = df['day_of_year'] - 1
    df['s_dayl'] = daylength[yday]
    tdew_save = tdew
    pva_save = pva

    # FIXME: This function has lots of inputs and outputs
    tdew, pet = _compute_srad_humidity_onetime(tdew, pva, tt_max0, flat_potrad,
                                               slope_potrad, sky_prop,
                                               daylength, parray, pa, dtr, df)

    sum_pet = pet.values.sum()
    ann_pet = (sum_pet / n_days) * consts['DAYS_PER_YEAR']

    # FIXME: Another really long conditional
    if (('tdew' in df) or ('s_hum' in df)
            or (options['VP_ITER'].upper() == 'VP_ITER_ANNUAL'
                and ann_pet / ann_precip >= 2.5)):
        tdew = tdew_save[:]
        pva = pva_save[:]

    # FIXME: Another really long conditional
    #if (options['VP_ITER'].upper() == 'VP_ITER_ALWAYS' or
    #    (options['VP_ITER'].upper() == 'VP_ITER_ANNUAL' and
    #     ann_pet / ann_precip >= 2.5) or
    #        options['VP_ITER'].upper() == 'VP_ITER_CONVERGE'):
    #    if (options['VP_ITER'].upper() == 'VP_ITER_CONVERGE'):
    #        max_iter = 100
    #    else:
    #        max_iter = 2
    #else:
    #    max_iter = 1

    #FIXME Still want to reduce the number of args here
    #FIXME This also takes up the majority of the mtclim runtime
    rmse_tdew = tol + 1

    #f = lambda x : rmse(_compute_srad_humidity_onetime(x, pva, tt_max0, flat_potrad,
    #                                     slope_potrad, sky_prop, daylength,
    #                                     parray, pa, dtr, df)[0], tdew)
    def f(x):
        tdew_calc = _compute_srad_humidity_onetime(x, pva, tt_max0,
                                                   flat_potrad, slope_potrad,
                                                   sky_prop, daylength, parray,
                                                   pa, dtr, df)[0]
        print(tdew_calc - tdew)
        err = rmse(tdew_calc, tdew)
        print(err)
        return err

    res = minimize(f, tdew, tol=rmse_tdew)
    tdew = res.x
    pva = svp(tdew)
    if 's_hum' not in df:
        df['s_hum'] = pva

    pvs = svp(df['s_t_day'])
    vpd = pvs - pva
    df['s_vpd'] = np.maximum(vpd, 0.)
Exemplo n.º 9
0
def vapor_pressure(tdew):
    return svp(tdew)