예제 #1
0
def test_persistence_scalar_index_invalid_times_instant(site_metadata):
    data = pd.Series(100., index=[0])
    load_data = partial(load_data_base, data)
    tz = 'America/Phoenix'
    interval_label = 'instant'
    observation = default_observation(site_metadata,
                                      interval_length='5min',
                                      interval_label=interval_label)
    # instant obs that cover the whole interval - not allowed!
    data_start = pd.Timestamp('20190404 1200', tz=tz)
    data_end = pd.Timestamp('20190404 1300', tz=tz)
    forecast_start = pd.Timestamp('20190404 1300', tz=tz)
    forecast_end = pd.Timestamp('20190404 1400', tz=tz)
    interval_length = pd.Timedelta('30min')
    with pytest.raises(ValueError):
        persistence.persistence_scalar_index(observation, data_start, data_end,
                                             forecast_start, forecast_end,
                                             interval_length, interval_label,
                                             load_data)
def test_persistence_scalar_index_invalid_times_invalid_label(site_metadata):
    data = pd.Series(100., index=[0])
    load_data = partial(load_data_base, data)
    tz = 'America/Phoenix'
    interval_length = pd.Timedelta('30min')

    interval_label = 'invalid'
    observation = default_observation(site_metadata, interval_length='5min')
    object.__setattr__(observation, 'interval_label', interval_label)
    data_start = pd.Timestamp('20190404 1200', tz=tz)
    data_end = pd.Timestamp('20190404 1300', tz=tz)
    forecast_start = pd.Timestamp('20190404 1300', tz=tz)
    forecast_end = pd.Timestamp('20190404 1400', tz=tz)
    with pytest.raises(ValueError) as excinfo:
        persistence.persistence_scalar_index(observation, data_start, data_end,
                                             forecast_start, forecast_end,
                                             interval_length, interval_label,
                                             load_data)
    assert "invalid interval_label" in str(excinfo.value)
예제 #3
0
def test_persistence_scalar_index_low_solar_elevation(site_metadata,
                                                      powerplant_metadata):

    interval_label = 'beginning'
    observation = default_observation(site_metadata,
                                      interval_length='5min',
                                      interval_label=interval_label)
    observation_ac = default_observation(powerplant_metadata,
                                         interval_length='5min',
                                         interval_label=interval_label,
                                         variable='ac_power')

    # at ABQ Baseline, solar apparent zenith for these points is
    # 2019-05-13 12:00:00+00:00     91.62
    # 2019-05-13 12:05:00+00:00     90.09
    # 2019-05-13 12:10:00+00:00     89.29
    # 2019-05-13 12:15:00+00:00     88.45
    # 2019-05-13 12:20:00+00:00     87.57
    # 2019-05-13 12:25:00+00:00     86.66

    tz = 'UTC'
    data_start = pd.Timestamp('20190513 1200', tz=tz)
    data_end = pd.Timestamp('20190513 1230', tz=tz)
    index = pd.date_range(start=data_start,
                          end=data_end,
                          freq='5min',
                          closed='left')

    # clear sky 5 min avg (from 1 min avg) GHI is
    # [0., 0.10932908, 1.29732454, 4.67585122, 10.86548521, 19.83487399]
    # create data series that could produce obs / clear of
    # 0/0, 1/0.1, -1/1.3, 5/5, 10/10, 20/20
    # average without limits is (10 - 1 + 1 + 1 + 1) / 5 = 2.4
    # average with element limits of [0, 2] = (2 + 0 + 1 + 1 + 1) / 5 = 1

    data = pd.Series([0, 1, -1, 5, 10, 20.], index=index)
    forecast_start = pd.Timestamp('20190513 1230', tz=tz)
    forecast_end = pd.Timestamp('20190513 1300', tz=tz)
    interval_length = pd.Timedelta('5min')
    load_data = partial(load_data_base, data)

    expected_index = pd.date_range(start=forecast_start,
                                   end=forecast_end,
                                   freq='5min',
                                   closed='left')

    # clear sky 5 min avg GHI is
    # [31.2, 44.5, 59.4, 75.4, 92.4, 110.1]
    expected_vals = [31.2, 44.5, 59.4, 75.4, 92.4, 110.1]
    expected = pd.Series(expected_vals, index=expected_index)

    fx = persistence.persistence_scalar_index(observation, data_start,
                                              data_end, forecast_start,
                                              forecast_end, interval_length,
                                              interval_label, load_data)
    assert_series_equal(fx, expected, check_less_precise=1, check_names=False)

    expected = pd.Series([0.2, 0.7, 1.2, 1.6, 2., 8.9], index=expected_index)
    fx = persistence.persistence_scalar_index(observation_ac, data_start,
                                              data_end, forecast_start,
                                              forecast_end, interval_length,
                                              interval_label, load_data)
    assert_series_equal(fx, expected, check_less_precise=1, check_names=False)
예제 #4
0
def run_persistence(session,
                    observation,
                    forecast,
                    run_time,
                    issue_time,
                    index=False):
    """
    Run a persistence *forecast* for an *observation*.

    For intraday forecasts, the *index* argument controls if the
    forecast is constructed using persistence of the measured values
    (*index = False*) or persistence using clear sky index or AC power
    index.

    For day ahead forecasts, only persistence of measured values
    (*index = False*) is supported.

    Forecasts may be run operationally or retrospectively. For
    operational forecasts, *run_time* is typically set to now. For
    retrospective forecasts, *run_time* is the time by which the
    forecast should be run so that it could have been be delivered for
    the *issue_time*. Forecasts will only use data with timestamps
    before *run_time*.

    The persistence *window* is the time over which the persistence
    quantity (irradiance, power, clear sky index, or power index) is
    averaged. The persistence window is automatically determined
    from the *forecast* attributes:

      * Intraday persistence forecasts:
           *window = forecast.run_length*.
           No longer than 1 hour.
      * Day ahead forecasts (all but net load) and week ahead forecasts (net
        load only):
          *window = forecast.interval_length*.

    Users that would like more flexibility may use the lower-level
    functions in
    :py:mod:`solarforecastarbiter.reference_forecasts.persistence`.

    Parameters
    ----------
    session : api.Session
        The session object to use to request data from the
        SolarForecastArbiter API.
    observation : datamodel.Observation
        The metadata of the observation to be used to create the
        forecast.
    forecast : datamodel.Forecast
        The metadata of the desired forecast.
    run_time : pd.Timestamp
        Run time of the forecast.
    issue_time : pd.Timestamp
        Issue time of the forecast run.
    index : bool, default False
        If False, use persistence of observed value. If True, use
        persistence of clear sky or AC power index.

    Returns
    -------
    forecast : pd.Series
        Forecast conforms to the metadata specified by the *forecast*
        argument.

    Raises
    ------
    ValueError
        If forecast and issue_time are incompatible.
    ValueError
        If persistence window < observation.interval_length.
    ValueError
        If forecast.run_length = 1 day and forecast period is not
        midnight to midnight.
    ValueError
        If forecast.run_length = 1 day and index=True.
    ValueError
        If instantaneous forecast and instantaneous observation interval
        lengths do not match.
    ValueError
        If average observations are used to make instantaneous forecast.

    Notes
    -----
    For non-intraday net load forecasts, this function will use a weekahead
    persistence due to the fact that net load exhibits stronger correlation
    week-to-week than day-to-day. For example, the net load on a Monday tends
    to look more similar to the previous Monday that it does to the previous
    day (Sunday).
    """
    utils.check_persistence_compatibility(observation, forecast, index)
    forecast_start, forecast_end = utils.get_forecast_start_end(
        forecast, issue_time, False)
    intraday = utils._is_intraday(forecast)
    if not intraday:
        # raise ValueError if not intraday and not midnight to midnight
        utils._check_midnight_to_midnight(forecast_start, forecast_end)

    data_start, data_end = utils.get_data_start_end(observation, forecast,
                                                    run_time)

    def load_data(observation, data_start, data_end):
        df = session.get_observation_values(observation.observation_id,
                                            data_start, data_end,
                                            observation.interval_label)
        df = df.tz_convert(observation.site.timezone)
        return df['value']

    if intraday and index:
        fx = persistence.persistence_scalar_index(
            observation, data_start, data_end, forecast_start, forecast_end,
            forecast.interval_length, forecast.interval_label, load_data)
    elif intraday and not index:
        fx = persistence.persistence_scalar(observation, data_start, data_end,
                                            forecast_start, forecast_end,
                                            forecast.interval_length,
                                            forecast.interval_label, load_data)
    elif not intraday and not index:
        fx = persistence.persistence_interval(observation, data_start,
                                              data_end, forecast_start,
                                              forecast.interval_length,
                                              forecast.interval_label,
                                              load_data)
    else:  # pragma: no cover
        raise ValueError(
            'index=True not supported for forecasts with run_length >= 1day')

    return fx
예제 #5
0
def run_persistence(session,
                    observation,
                    forecast,
                    run_time,
                    issue_time,
                    index=False,
                    load_data=None):
    """
    Run a persistence *forecast* for an *observation*.

    For intraday forecasts, the *index* argument controls if the
    forecast is constructed using persistence of the measured values
    (*index = False*) or persistence using clear sky index or AC power
    index.

    For day ahead forecasts, only persistence of measured values
    (*index = False*) is supported.

    Forecasts may be run operationally or retrospectively. For
    operational forecasts, *run_time* is typically set to now. For
    retrospective forecasts, *run_time* is the time by which the
    forecast should be run so that it could have been be delivered for
    the *issue_time*. Forecasts will only use data with timestamps
    before *run_time*.

    The persistence *window* is the time over which the persistence
    quantity (irradiance, power, clear sky index, or power index) is
    averaged. The persistence window is automatically determined
    from the *forecast* attributes:

    - Intraday persistence forecasts:

      + ``window = forecast.run_length``. No longer than 1 hour.

    - Day ahead forecasts (all but net load) and week ahead forecasts (net
      load only):

      + ``window = forecast.interval_length``.

    Users that would like more flexibility may use the lower-level
    functions in
    :py:mod:`solarforecastarbiter.reference_forecasts.persistence`.

    Parameters
    ----------
    session : api.Session
        The session object to use to request data from the
        SolarForecastArbiter API.
    observation : datamodel.Observation
        The metadata of the observation to be used to create the
        forecast.
    forecast : datamodel.Forecast
        The metadata of the desired forecast.
    run_time : pd.Timestamp
        Run time of the forecast.
    issue_time : pd.Timestamp
        Issue time of the forecast run.
    index : bool, default False
        If False, use persistence of observed value. If True, use
        persistence of clear sky or AC power index.
    load_data : function
        Function to load the observation data 'value' series given
        (observation, data_start, data_end) arguments. Typically,
        calls `session.get_observation_values` and selects the 'value'
        column. May also have data preloaded to then slice from
        data_start to data_end.

    Returns
    -------
    forecast : pd.Series
        Forecast conforms to the metadata specified by the *forecast*
        argument.

    Raises
    ------
    ValueError
        If forecast and issue_time are incompatible.
    ValueError
        If data is required from after run_time.
    ValueError
        If persistence window < observation.interval_length.
    ValueError
        If forecast.run_length => 1 day and index=True.
    ValueError
        If instantaneous forecast and instantaneous observation interval
        lengths do not match.
    ValueError
        If average observations are used to make instantaneous forecast.

    Notes
    -----
    For non-intraday net load forecasts, this function will use a weekahead
    persistence due to the fact that net load exhibits stronger correlation
    week-to-week than day-to-day. For example, the net load on a Monday tends
    to look more similar to the previous Monday that it does to the previous
    day (Sunday).
    """
    utils.check_persistence_compatibility(observation, forecast, index)
    forecast_start, forecast_end = utils.get_forecast_start_end(
        forecast, issue_time, False)
    intraday = utils._is_intraday(forecast)

    if load_data is None:
        load_data = _default_load_data(session)
    data_start, data_end = utils.get_data_start_end(observation, forecast,
                                                    run_time, issue_time)
    if data_end > run_time:
        raise ValueError(
            'Persistence forecast requires data from after run_time')

    if isinstance(forecast, datamodel.ProbabilisticForecast):
        cvs = [f.constant_value for f in forecast.constant_values]
        fx = persistence.persistence_probabilistic(
            observation, data_start, data_end, forecast_start, forecast_end,
            forecast.interval_length, forecast.interval_label, load_data,
            forecast.axis, cvs)
    elif intraday and index:
        fx = persistence.persistence_scalar_index(
            observation, data_start, data_end, forecast_start, forecast_end,
            forecast.interval_length, forecast.interval_label, load_data)
    elif intraday and not index:
        fx = persistence.persistence_scalar(observation, data_start, data_end,
                                            forecast_start, forecast_end,
                                            forecast.interval_length,
                                            forecast.interval_label, load_data)
    elif not intraday and not index:
        fx = persistence.persistence_interval(observation, data_start,
                                              data_end, forecast_start,
                                              forecast.interval_length,
                                              forecast.interval_label,
                                              load_data)
    else:  # pragma: no cover
        raise ValueError(
            'index=True not supported for forecasts with run_length >= 1day')

    return fx