Пример #1
0
def daily_temperature_range(tasmin: xarray.DataArray,
                            tasmax: xarray.DataArray,
                            freq: str = "YS") -> xarray.DataArray:
    r"""Mean of daily temperature range.

    The mean difference between the daily maximum temperature and the daily minimum temperature.

    Parameters
    ----------
    tasmin : xarray.DataArray
      Minimum daily temperature values [℃] or [K]
    tasmax : xarray.DataArray
      Maximum daily temperature values [℃] or [K]
    freq : str
      Resampling frequency; Defaults to "YS".

    Returns
    -------
    xarray.DataArray
      The average variation in daily temperature range for the given time period.

    Notes
    -----
    Let :math:`TX_{ij}` and :math:`TN_{ij}` be the daily maximum and minimum temperature at day :math:`i`
    of period :math:`j`. Then the mean diurnal temperature range in period :math:`j` is:

    .. math::

        DTR_j = \frac{ \sum_{i=1}^I (TX_{ij} - TN_{ij}) }{I}
    """
    q = 1 * units2pint(tasmax) - 0 * units2pint(tasmin)
    dtr = tasmax - tasmin
    out = dtr.resample(time=freq).mean(dim="time", keep_attrs=True)
    out.attrs["units"] = f"{q.units}"
    return out
Пример #2
0
    def test_units2pint(self, pr_series):
        u = units2pint(pr_series([1, 2]))
        assert (str(u)) == "kilogram / meter ** 2 / second"
        assert pint2cfunits(u) == "kg m-2 s-1"

        u = units2pint("m^3 s-1")
        assert str(u) == "meter ** 3 / second"
        assert pint2cfunits(u) == "m^3 s-1"

        u = units2pint("2 kg m-2 s-1")
        assert (str(u)) == "kilogram / meter ** 2 / second"

        u = units2pint("%")
        assert str(u) == "percent"
Пример #3
0
    def _check_same_units(*args):
        sim = args[0]
        ref = args[1]
        units_sim = units2pint(sim.units)
        units_ref = units2pint(ref.units)

        if units_sim != units_ref:
            warn(
                f" sim({units_sim}) and ref({units_ref}) don't have the same units."
                f" sim will be converted to {units_ref}."
            )
            sim = convert_units_to(sim, ref)
        out = func(sim, ref, *args[2:])
        return out
Пример #4
0
def ts_fit_graph(ts, params):
    """Create graphic showing an histogram of the data and the distribution fitted to it.

    Parameters
    ----------
    ts : str
      Path to netCDF file storing the time series.
    params : str
      Path to netCDF file storing the distribution parameters.

    Returns
    -------
    fig
    """
    from xclim.indices.stats import get_dist

    n = ts.nbasins.size
    dist = params.attrs["scipy_dist"]

    fig, axes = plt.subplots(n, figsize=(10, 6), squeeze=False)

    for i in range(n):
        ax = axes.flat[i]
        ax2 = plt.twinx(ax)
        p = params.isel(nbasins=i)

        # Plot histogram of time series as density then as a normal count.
        density, bins, patches = ax.hist(
            ts.isel(nbasins=i).dropna(dim="time"),
            alpha=0.5,
            density=True,
            bins="auto",
            label="__nolabel__",
        )
        ax2.hist(
            ts.isel(nbasins=i).dropna(dim="time"),
            bins=bins,
            facecolor=(1, 1, 1, 0.01),
            edgecolor="gray",
            linewidth=1,
        )

        # Plot pdf of distribution
        dc = get_dist(dist)(*params.isel(nbasins=i))
        mn = dc.ppf(0.01)
        mx = dc.ppf(0.99)
        q = np.linspace(mn, mx, 200)
        pdf = dc.pdf(q)
        ps = ", ".join(["{:.1f}".format(x) for x in p.values])
        ax.plot(q, pdf, "-", label="{}({})".format(params.attrs["scipy_dist"], ps))

        # Labels
        ax.set_xlabel("{} (${:~P}$)".format(ts.long_name, units2pint(ts.units)))
        ax.set_ylabel("Probability density")
        ax2.set_ylabel("Histogram count")

        ax.legend(frameon=False)

    plt.tight_layout()
    return fig
Пример #5
0
def daily_temperature_range(
    tasmin: xarray.DataArray,
    tasmax: xarray.DataArray,
    freq: str = "YS",
    op: str = "mean",
) -> xarray.DataArray:
    r"""Statistics of daily temperature range.

    The mean difference between the daily maximum temperature and the daily minimum temperature.

    Parameters
    ----------
    tasmin : xarray.DataArray
      Minimum daily temperature values [℃] or [K]
    tasmax : xarray.DataArray
      Maximum daily temperature values [℃] or [K]
    freq : str
      Resampling frequency; Defaults to "YS".
    op : str {'min', 'max', 'mean', 'std'} or func
      Reduce operation. Can either be a DataArray method or a function that can be applied to a DataArray.

    Returns
    -------
    xarray.DataArray
      The average variation in daily temperature range for the given time period.

    Notes
    -----
    For a default calculation using `op='mean'` :

    Let :math:`TX_{ij}` and :math:`TN_{ij}` be the daily maximum and minimum temperature at day :math:`i`
    of period :math:`j`. Then the mean diurnal temperature range in period :math:`j` is:

    .. math::

        DTR_j = \frac{ \sum_{i=1}^I (TX_{ij} - TN_{ij}) }{I}
    """
    q = 1 * units2pint(tasmax) - 0 * units2pint(tasmin)
    dtr = tasmax - tasmin
    out = select_resample_op(dtr, op=op, freq=freq)

    out.attrs["units"] = f"{q.units}"
    return out
Пример #6
0
def precip_seasonality(pr: xarray.DataArray,
                       freq: str = "YS") -> xarray.DataArray:
    r"""ANUCLIM Precipitation Seasonality (C of V).

    The annual precipitation Coefficient of Variation (C of V) expressed in percent. Calculated as the standard deviation
    of precipitation values for a given year expressed as a percentage of the mean of those values.

    Parameters
    ----------
    pr : xarray.DataArray
      Total precipitation rate at daily, weekly, or monthly frequency.
      Units need to be defined as a rate (e.g. mm d-1, mm week-1).
    freq : str
      Resampling frequency.

    Returns
    -------
    xarray.DataArray, [%]
      Precipitation coefficient of variation

    Examples
    --------
    The following would compute for each grid cell of file `pr.day.nc` the annual precipitation seasonality:

    >>> import xclim.indices as xci
    >>> p = xr.open_dataset(path_to_pr_file).pr
    >>> pday_seasonality = xci.precip_seasonality(p)
    >>> p_weekly = xci.precip_accumulation(p, freq='7D')

    # Input units need to be a rate
    >>> p_weekly.attrs['units'] = "mm/week"
    >>> pweek_seasonality = xci.precip_seasonality(p_weekly)

    Notes
    -----
    According to the ANUCLIM user-guide https://fennerschool.anu.edu.au/files/anuclim61.pdf (ch. 6), input
    values should be at a weekly (or monthly) frequency.  However, the xclim.indices implementation here will calculate
    the result with input data with daily frequency as well. As such weekly or monthly input values, if desired,
    should be calculated prior to calling the function.

    If input units are in mm s-1 (or equivalent) values are converted to mm/day to avoid potentially small denominator
    values.
    """
    # If units in mm/sec convert to mm/days to avoid potentially small denominator
    if units2pint(pr) == units("mm / s"):
        pr = convert_units_to(pr, "mm d-1")

    with xarray.set_options(keep_attrs=True):
        seas = 100 * _anuclim_coeff_var(pr, freq=freq)

    seas.attrs["units"] = "%"
    return seas
Пример #7
0
def extreme_temperature_range(tasmin: xarray.DataArray,
                              tasmax: xarray.DataArray,
                              freq: str = "YS") -> xarray.DataArray:
    r"""Extreme intra-period temperature range.

    The maximum of max temperature (TXx) minus the minimum of min temperature (TNn) for the given time period.

    Parameters
    ----------
    tasmin : xarray.DataArray
      Minimum daily temperature values [℃] or [K]
    tasmax : xarray.DataArray
      Maximum daily temperature values [℃] or [K]
    freq : Optional[str[
      Resampling frequency; Defaults to "YS".

    Returns
    -------
    xarray.DataArray
      Extreme intra-period temperature range for the given time period.

    Notes
    -----
    Let :math:`TX_{ij}` and :math:`TN_{ij}` be the daily maximum and minimum temperature at day :math:`i`
    of period :math:`j`. Then the extreme temperature range in period :math:`j` is:

    .. math::

        ETR_j = max(TX_{ij}) - min(TN_{ij})
    """
    q = 1 * units2pint(tasmax) - 0 * units2pint(tasmin)

    tx_max = tasmax.resample(time=freq).max(dim="time")
    tn_min = tasmin.resample(time=freq).min(dim="time")

    out = tx_max - tn_min
    out.attrs["units"] = f"{q.units}"
    return out
Пример #8
0
def xclim_units_any2pint(ds, var):
    """
    Parameters
    ----------
    ds : xr.Dataset
    var : str

    Returns
    -------
    xr.Dataset with `var` units str attribute converted to xclim's pint registry format
    """

    logger.info(f"Reformatting {var} unit string representation")
    ds[var].attrs["units"] = str(xclim_units.units2pint(ds[var].attrs["units"]))
    return ds
Пример #9
0
def daily_temperature_range_variability(tasmin: xarray.DataArray,
                                        tasmax: xarray.DataArray,
                                        freq: str = "YS") -> xarray.DataArray:
    r"""Mean absolute day-to-day variation in daily temperature range.

    Mean absolute day-to-day variation in daily temperature range.

    Parameters
    ----------
    tasmin : xarray.DataArray
      Minimum daily temperature values [℃] or [K]
    tasmax : xarray.DataArray
      Maximum daily temperature values [℃] or [K]
    freq : str
      Resampling frequency; Defaults to "YS".

    Returns
    -------
    xarray.DataArray
      The average day-to-day variation in daily temperature range for the given time period.

    Notes
    -----
    Let :math:`TX_{ij}` and :math:`TN_{ij}` be the daily maximum and minimum temperature at
    day :math:`i` of period :math:`j`. Then calculated is the absolute day-to-day differences in
    period :math:`j` is:

    .. math::

       vDTR_j = \frac{ \sum_{i=2}^{I} |(TX_{ij}-TN_{ij})-(TX_{i-1,j}-TN_{i-1,j})| }{I}
    """
    q = 1 * units2pint(tasmax) - 0 * units2pint(tasmin)
    vdtr = abs((tasmax - tasmin).diff(dim="time"))
    out = vdtr.resample(time=freq).mean(dim="time")
    out.attrs["units"] = f"{q.units}"
    return out
Пример #10
0
    def test_units2pint(self, pr_series):
        u = units2pint(pr_series([1, 2]))
        assert (str(u)) == "kilogram / meter ** 2 / second"
        assert pint2cfunits(u) == "kg m-2 s-1"

        u = units2pint("m^3 s-1")
        assert str(u) == "meter ** 3 / second"
        assert pint2cfunits(u) == "m^3 s-1"

        u = units2pint("kg m-2 s-1")
        assert (str(u)) == "kilogram / meter ** 2 / second"

        u = units2pint("%")
        assert str(u) == "percent"

        u = units2pint("1")
        assert str(u) == "dimensionless"

        u = units2pint("mm s-1")
        assert str(u) == "millimeter / second"

        u = units2pint("degrees_north")
        assert str(u) == "degrees_north"
Пример #11
0
def humidex(
    tas: xr.DataArray,
    tdps: Optional[xr.DataArray] = None,
    hurs: Optional[xr.DataArray] = None,
) -> xr.DataArray:
    r"""Humidex index.

    The humidex indicates how hot the air feels to an average person, accounting for the effect of humidity. It
    can be loosely interpreted as the equivalent perceived temperature when the air is dry.

    Parameters
    ----------
    tas : xarray.DataArray
      Air temperature.
    tdps : xarray.DataArray,
      Dewpoint temperature.
    hurs : xarray.DataArray
      Relative humidity.

    Returns
    -------
    xarray.DataArray, [temperature]
      The humidex index.

    Notes
    -----
    The humidex is usually computed using hourly observations of dry bulb and dewpoint temperatures. It is computed
    using the formula based on [masterton79]_:

    .. math::

       T + {\frac {5}{9}}\left[e - 10\right]

    where :math:`T` is the dry bulb air temperature (°C). The term :math:`e` can be computed from the dewpoint
    temperature :math:`T_{dewpoint}` in °K:

    .. math::

       e = 6.112 \times \exp(5417.7530\left({\frac {1}{273.16}}-{\frac {1}{T_{\text{dewpoint}}}}\right)

    where the constant 5417.753 reflects the molecular weight of water, latent heat of vaporization,
    and the universal gas constant ([mekis15]_). Alternatively, the term :math:`e` can also be computed from
    the relative humidity `h` expressed in percent using [sirangelo20]_:

    .. math::

      e = \frac{h}{100} \times 6.112 * 10^{7.5 T/(T + 237.7)}.

    The humidex *comfort scale* ([eccc]_) can be interpreted as follows:

    - 20 to 29 : no discomfort;
    - 30 to 39 : some discomfort;
    - 40 to 45 : great discomfort, avoid exertion;
    - 46 and over : dangerous, possible heat stroke;

    Please note that while both the humidex and the heat index are calculated
    using dew point, the humidex uses a dew point of 7 °C (45 °F) as a base,
    whereas the heat index uses a dew point base of 14 °C (57 °F). Further,
    the heat index uses heat balance equations which account for many variables
    other than vapor pressure, which is used exclusively in the humidex
    calculation.

    References
    ----------
    .. [masterton79] Masterton, J. M., & Richardson, F. A. (1979). HUMIDEX, A method of quantifying human discomfort due to excessive heat and humidity, CLI 1-79. Downsview, Ontario: Environment Canada, Atmospheric Environment Service.
    .. [mekis15] Éva Mekis, Lucie A. Vincent, Mark W. Shephard & Xuebin Zhang (2015) Observed Trends in Severe Weather Conditions Based on Humidex, Wind Chill, and Heavy Rainfall Events in Canada for 1953–2012, Atmosphere-Ocean, 53:4, 383-397, DOI: 10.1080/07055900.2015.1086970
    .. [sirangelo20] Sirangelo, B., Caloiero, T., Coscarelli, R. et al. Combining stochastic models of air temperature and vapour pressure for the analysis of the bioclimatic comfort through the Humidex. Sci Rep 10, 11395 (2020). https://doi.org/10.1038/s41598-020-68297-4
    .. [eccc] https://climate.weather.gc.ca/glossary_e.html
    """
    if (tdps is None) == (hurs is None):
        raise ValueError(
            "At least one of `tdps` or `hurs` must be given, and not both.")

    # Vapour pressure in hPa
    if tdps is not None:
        # Convert dewpoint temperature to Kelvins
        tdps = convert_units_to(tdps, "kelvin")
        e = 6.112 * np.exp(5417.7530 * (1 / 273.16 - 1.0 / tdps))

    elif hurs is not None:
        # Convert dry bulb temperature to Celsius
        tasC = convert_units_to(tas, "celsius")
        e = hurs / 100 * 6.112 * 10**(7.5 * tasC / (tasC + 237.7))

    # Temperature delta due to humidity in delta_degC
    h = 5 / 9 * (e - 10)
    h.attrs["units"] = "delta_degree_Celsius"

    # Get delta_units for output
    du = (1 * units2pint(tas) - 0 * units2pint(tas)).units
    h = convert_units_to(h, du)

    # Add the delta to the input temperature
    out = h + tas
    out.attrs["units"] = tas.units
    return out