Beispiel #1
0
def pdsi(precip_time_series: np.ndarray,
         pet_time_series: np.ndarray,
         awc,
         data_start_year: int,
         calibration_start_year: int,
         calibration_end_year: int):
    """
    This function computes the Palmer Drought Severity Index (PDSI), Palmer
    Hydrological Drought Index (PHDI), and Palmer Z-Index.

    :param precip_time_series: time series of monthly precipitation values, in inches
    :param pet_time_series: time series of monthly PET values, in inches
    :param awc: available water capacity (soil constant), in inches
    :param data_start_year: initial year of the input precipitation and PET datasets,
                            both of which are assumed to start in January of this year
    :param calibration_start_year: initial year of the calibration period
    :param calibration_end_year: final year of the calibration period
    :return: four numpy arrays containing PDSI, PHDI, PMDI, and Z-Index values respectively
    """

    return palmer.pdsi(precip_time_series,
                       pet_time_series,
                       awc,
                       data_start_year,
                       calibration_start_year,
                       calibration_end_year)
Beispiel #2
0
    def test_pdsi(self):

        pdsi, phdi, pmdi, zindex = palmer.pdsi(
            self.fixture_precips_mm_monthly, self.fixture_pet_mm,
            self.fixture_awc_inches, self.fixture_data_year_start_monthly,
            self.fixture_calibration_year_start_monthly,
            self.fixture_calibration_year_end_monthly)

        np.testing.assert_allclose(
            pdsi,
            self.fixture_palmer_pdsi_monthly,
            atol=0.001,
            equal_nan=True,
            err_msg='PDSI not computed as expected from monthly inputs')

        np.testing.assert_allclose(
            phdi,
            self.fixture_palmer_phdi_monthly,
            atol=0.001,
            equal_nan=True,
            err_msg='PHDI not computed as expected from monthly inputs')

        np.testing.assert_allclose(
            pmdi,
            self.fixture_palmer_pmdi_monthly,
            atol=0.001,
            equal_nan=True,
            err_msg='PMDI not computed as expected from monthly inputs')

        np.testing.assert_allclose(
            zindex,
            self.fixture_palmer_zindex_monthly,
            atol=0.001,
            equal_nan=True,
            err_msg='Z-Index not computed as expected from monthly inputs')
def test_pdsi(
    precips_mm_monthly,
    pet_thornthwaite_mm,
    awc_inches,
    data_year_start_monthly,
    calibration_year_start_monthly,
    calibration_year_end_monthly,
    palmer_pdsi_monthly,
    palmer_phdi_monthly,
    palmer_pmdi_monthly,
    palmer_zindex_monthly,
):

    pdsi, phdi, pmdi, zindex = palmer.pdsi(
        precips_mm_monthly,
        pet_thornthwaite_mm,
        awc_inches,
        data_year_start_monthly,
        calibration_year_start_monthly,
        calibration_year_end_monthly,
    )

    np.testing.assert_allclose(
        pdsi,
        palmer_pdsi_monthly,
        atol=0.001,
        equal_nan=True,
        err_msg="PDSI not computed as expected from monthly inputs",
    )

    np.testing.assert_allclose(
        phdi,
        palmer_phdi_monthly,
        atol=0.001,
        equal_nan=True,
        err_msg="PHDI not computed as expected from monthly inputs",
    )

    np.testing.assert_allclose(
        pmdi,
        palmer_pmdi_monthly,
        atol=0.001,
        equal_nan=True,
        err_msg="PMDI not computed as expected from monthly inputs",
    )

    np.testing.assert_allclose(
        zindex,
        palmer_zindex_monthly,
        atol=0.001,
        equal_nan=True,
        err_msg="Z-Index not computed as expected from monthly inputs",
    )
def pdsi(
    ppt: pd.Series,
    pet: pd.Series,
    awc: float,
    pad_years: int = 10,
    y1: int = CLIMATE_NORMAL_PERIOD[0],
    y2: int = CLIMATE_NORMAL_PERIOD[1],
) -> np.ndarray:
    """Calculate the Palmer Drought Severity Index (PDSI)

    This is a simple wrapper of the climate_idicies package implementation of pdsi. The wrapper
    includes a spin up period (`pad_years`) using a repeated climatology calculated between `y1`
    and `y2`.

    Note that this is not a perfect reproduction of the Terraclimate PDSI implementation. See
    https://github.com/carbonplan/cmip6-downscaling/issues/4 for more details.

    Parameters
    ----------
    ppt : pd.Series
        Monthly precipitation timeseries (mm)
    pet : pd.Series
        Monthly PET timeseries (mm)
    awc : float
        Soil water capacity (mm)
    pad_years : int
        Number of years of the climatology to prepend to the timeseries of ppt and pet
    y1 : int
        Start year for climate normal period
    y2 : int
        End year for climate normal period

    Returns
    -------
    pdsi : pd.Series
        Timeseries of PDSI (unitless)
    """

    pad_months = pad_years * MONTHS_PER_YEAR
    assert len(ppt) > pad_months
    y0 = ppt.index.year[0] - pad_years  # start year (with pad)

    awc_in = awc / MM_PER_IN

    # calculate the climatology for ppt and pet (for only the climate normal period)
    df = pd.concat([pet, ppt], axis=1)
    climatology = df.loc[str(y1) : str(y2)].groupby(by=by_month).mean()

    # repeat climatology for pad_years, then begine the time series
    ppt_extended = np.concatenate([np.tile(climatology['ppt'].values, pad_years), ppt.values])
    ppt_extended_in = ppt_extended / MM_PER_IN
    pet_extended = np.concatenate([np.tile(climatology['pet'].values, pad_years), pet.values])
    pet_extended_in = pet_extended / MM_PER_IN

    # set all zero ppt months to SMALL_PPT: this gets around a divide by zero
    # in the pdsi function below.
    ppt_zeros = ppt_extended_in <= 0
    if ppt_zeros.any():
        ppt_extended_in[ppt_zeros] = SMALL_PPT

    pdsi_vals = palmer.pdsi(ppt_extended_in, pet_extended_in, awc_in, y0, y1, y2)[0]
    pdsi_vals = pdsi_vals.clip(-16, 16)
    out = pd.Series(pdsi_vals[pad_months:], index=ppt.index)

    return out