示例#1
0
def _clipping_power(ac_power, slope_max, power_min, power_quantile, freq=None):
    # Calculate a power threshold, above which the power is being
    # clipped.
    #
    # - The daytime power curve is calculated using
    #   _daytime_powercurve(). This function groups `ac_power` by
    #   minute of the day and returns the `power_quantile`-quantile of
    #   power for each minute.
    #
    # - Each timestamp in the daytime power curve that satisfies the
    #   clipping criteria[*] is flagged.
    #
    # - The clipping threshold is calculated as the mean power during the
    #   longest flagged period in the daytime power curve.
    #
    # [*] clipping criteria: a timestamp satisfies the clipping
    # criteria if the absolute value of the slope of the daytime power curve
    # is less than `slope_max` and the value of the daytime
    # power curve is greater than `power_min` times the median of the
    # daytime power curve.
    #
    # Based on the PVFleets QA Analysis project
    if not freq:
        freq = util.freq_to_timedelta(pd.infer_freq(
            ac_power.index)).seconds / 60
    elif isinstance(freq, str):
        freq = util.freq_to_timedelta(freq).seconds / 60

    # Use the slope of the 99.5% quantile of daytime power at
    # each minute to identify clipping.
    powercurve = _daytime_powercurve(ac_power, power_quantile)
    normalized_power = powercurve / powercurve.max()
    power_slope = (normalized_power.diff() /
                   normalized_power.index.to_series().diff()) * freq

    clipped_times = _clipped(powercurve, power_slope,
                             powercurve.median() * power_min, slope_max)
    clipping_cumsum = (~clipped_times).cumsum()
    # get the value of the cumulative sum of the longest True span
    longest_clipped = clipping_cumsum.value_counts().idxmax()
    # select the longest span that satisfies the clipping criteria
    longest = powercurve[clipping_cumsum == longest_clipped]

    if longest.index.max() - longest.index.min() >= 60:
        # if the period of clipping is at least 60 minutes then we
        # have enough evidence to determine the clipping threshold.
        return longest.mean()
    return None
示例#2
0
def _freq_minutes(index, freq):
    """Return the frequency in minutes for `freq`. If `freq` is None
    then use the frequency inferred from `index`."""
    if freq is None:
        freq = pd.infer_freq(index)
    if freq is None:
        raise ValueError("cannot infer frequency")
    return util.freq_to_timedelta(freq).seconds / 60
示例#3
0
def fixed(ghi, daytime, clearsky, interval=None, min_gradient=2):
    """Detects shadows from fixed structures such as wires and poles.

    Uses morphological image processing methods to identify shadows
    from fixed local objects in GHI data. GHI data are assumed to
    be reasonably complete with relatively few missing values and at a
    fixed time interval nominally of 1 minute over the course of
    several months. Detection focuses on shadows with relatively short
    duration. The algorithm forms a 2D image of the GHI data by
    arranging time of day along the x-axis and day of year along the
    y-axis. Rapid change in GHI in the x-direction is used to identify
    edges of shadows; continuity in the y-direction is used to
    separate local object shading from cloud shadows.

    Parameters
    ----------
    ghi : Series
        Time series of GHI measurements. Data must be in local time at
        1-minute frequency and should cover at least 60 days.
    daytime : Series
        Boolean series with True for times when the sun is up.
    clearsky : Series
        Clearsky GHI with same index as `ghi`.
    interval : int, optional
        Interval between data points in minutes. If not specified the
        interval is inferred from the frequency of the index of `ghi`.
    min_gradient : float, default 2
        Threshold value for the morphological gradient [3]_.

    Returns
    -------
    Series
        Boolean series with true for times that are impacted by shadows.
    ndarray
        A boolean image (black and white) showing the shadows that were
        detected.

    References
    ----------

    .. [1] Martin, C. E., Hansen, C. W., An Image Processing Algorithm to
       Identify Near-Field Shading in Irradiance Measurements, preprint 2016
    .. [2] Reno, M.J. and C.W. Hansen, "Identification of periods of clear sky
       irradiance in time series of GHI measurements" Renewable Energy, v90,
       p. 520-531, 2016.
    .. [3] https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.morphological_gradient.html
    """  # noqa: E501
    if interval is None:
        interval = util.freq_to_timedelta(pd.infer_freq(
            ghi.index)).seconds // 60
    if interval != 1:
        raise ValueError("Data must be at 1-minute intervals")
    ghi_image, clearsky_image, clouds_image, index = _prepare_images(
        ghi, clearsky, daytime, interval)
    # normalize the GHI and dampen the dynamic range where the clear
    # sky model may have large errors (e.g. at very low sun elevation)
    alpha = 2000
    ghi_boosted = 1000 * (ghi_image + alpha) / (clearsky_image + alpha)

    # We must use scipy.ndimage here because skimage does not support
    # floating point data outside the range [-1, 1].
    gradient = ndimage.morphological_gradient(ghi_boosted, size=(1, 3))
    threshold = gradient > min_gradient  # binary image of wire candidates

    # From here we CAN use skimage because we are working with binary images.
    three_minute_mask = morphology.rectangle(1, 3)
    wires = morphology.remove_small_objects(
        morphology.binary_closing(threshold, three_minute_mask),
        min_size=200,
        connectivity=2  # all neighbors (including diagonals)
    )
    wires_image = _clean_wires(wires)
    wires_series = _to_series(wires, index)
    wires_series = wires_series.reindex(ghi.index, fill_value=False)
    return wires_series, wires_image
示例#4
0
def _freqstr_to_hours(freq):
    # Convert pandas freqstr to hours (as a float)
    return util.freq_to_timedelta(freq).seconds / 3600
示例#5
0
def _to_hours(freqstr):
    return util.freq_to_timedelta(freqstr).seconds / 3600
示例#6
0
def _freq_to_seconds(freq):
    delta = util.freq_to_timedelta(freq)
    return delta.days * (1440 * 60) + delta.seconds
示例#7
0
def _freqstr_to_minutes(freqstr):
    return util.freq_to_timedelta(freqstr).seconds / 60