Example #1
0
    def get_coordinate(self, ds=None):
        """Return the coordinate as in the output of group.apply.

        Currently, only implemented for groupings with prop == month or dayofyear.
        For prop == dayfofyear, a ds (Dataset or DataArray) can be passed to infer
        the max doy from the available years and calendar.
        """
        if self.prop == "month":
            return xr.DataArray(np.arange(1, 13),
                                dims=("month", ),
                                name="month")
        if self.prop == "season":
            return xr.DataArray(["DJF", "MAM", "JJA", "SON"],
                                dims=("season", ),
                                name="season")
        if self.prop == "dayofyear":
            if ds is not None:
                cal = get_calendar(ds, dim=self.dim)
                mdoy = max(
                    days_in_year(yr, cal)
                    for yr in np.unique(ds[self.dim].dt.year))
            else:
                mdoy = 365
            return xr.DataArray(np.arange(1, mdoy + 1),
                                dims=("dayofyear"),
                                name="dayofyear")
        if self.prop == "group":
            return xr.DataArray([1], dims=("group", ), name="group")
        # TODO woups what happens when there is no group? (prop is None)
        raise NotImplementedError()
Example #2
0
def day_lengths(
    dates: xr.DataArray,
    lat: xr.DataArray,
    obliquity: float = -0.4091,
    summer_solstice: DayOfYearStr = "06-21",
    start_date: Optional[Union[xarray.DataArray, DayOfYearStr]] = None,
    end_date: Optional[Union[xarray.DataArray, DayOfYearStr]] = None,
    freq: str = "YS",
) -> xr.DataArray:
    r"""Day-lengths according to latitude, obliquity, and day of year.

    Parameters
    ----------
    dates: xr.DataArray
    lat: xarray.DataArray
      Latitude coordinate.
    obliquity: float
      Obliquity of the elliptic (radians). Default: -0.4091.
    summer_solstice: DayOfYearStr
      Date of summer solstice in northern hemisphere. Used for approximating solar julian dates.
    start_date: xarray.DataArray or DayOfYearStr, optional
      Start date to consider for calculating mean day lengths. Default: None.
    end_date: xarray.DataArray or DayOfYearStr, optional
      End date to consider for calculating mean day lengths. Default: None.
    freq : str
      Resampling frequency.

    Returns
    -------
    xarray.DataArray
      If start and end date provided, returns total sum of daylight-hour between dates at provided frequency.
      If no start and end date provided, returns day-length in hours per individual day.

    Notes
    -----
    Daylight-hours are dependent on latitude, :math:`lat`, the Julian day (solar day) from the summer solstice in the
    Northern hemisphere, :math:`Jday`, and the axial tilt :math:`Axis`, therefore day-length at any latitude for a given
    date on Earth, :math:`dayLength_{lat_{Jday}}`, for a given year in days, :math:`Year`, can be approximated as
    follows:

    .. math::
        dayLength_{lat_{Jday}} = f({lat}, {Jday}) = \frac{\arccos(1-m_{lat_{Jday}})}{\pi} * 24

    Where:

    .. math::
        m_{lat_{Jday}} = f({lat}, {Jday}) = 1 - \tan({Lat}) * \tan \left({Axis}*\cos\left[\frac{2*\pi*{Jday}}{||{Year}||} \right] \right)

    The total sum of daylight hours for a given period between two days (:math:`{Jday} = 0` -> :math:`N`) within a solar
    year then is:

    .. math::
        \sum({SeasonDayLength_{lat}}) = \sum_{Jday=1}^{N} dayLength_{lat_{Jday}}

    References
    ----------
    Modified day-length equations for Huglin heliothermal index published in Hall, A., & Jones, G. V. (2010). Spatial
    analysis of climate in winegrape-growing regions in Australia. Australian Journal of Grape and Wine Research, 16(3),
    389‑404. https://doi.org/10.1111/j.1755-0238.2010.00100.x

    Examples available from Glarner, 2006 (http://www.gandraxa.com/length_of_day.xml).
    """
    cal = get_calendar(dates)

    year_length = dates.time.copy(
        data=[days_in_year(x, calendar=cal) for x in dates.time.dt.year])

    julian_date_from_solstice = dates.time.copy(data=doy_to_days_since(
        dates.time.dt.dayofyear, start=summer_solstice, calendar=cal))

    m_lat_dayofyear = 1 - np.tan(np.radians(lat)) * np.tan(obliquity * (np.cos(
        (2 * np.pi * julian_date_from_solstice) / year_length)))

    day_length_hours = (np.arccos(1 - m_lat_dayofyear) / np.pi) * 24

    if start_date and end_date:
        return aggregate_between_dates(day_length_hours,
                                       start=start_date,
                                       end=end_date,
                                       op="sum",
                                       freq=freq)
    else:
        return day_length_hours
Example #3
0
def test_days_in_year(year, calendar, exp):
    assert days_in_year(year, calendar) == exp