Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identify days where system output looks like tracking or fixed #52

Merged
merged 50 commits into from
Jul 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
a289297
Rough first cut laying out scaffolding for daily orientation checks
wfvining Jun 2, 2020
8a2c042
Add copyright and attribution
wfvining Jun 4, 2020
568202f
First tests for sunny_days
wfvining Jun 4, 2020
fff19a4
Get first two tests passing.
wfvining Jun 4, 2020
abb9474
Remove print statements
wfvining Jun 4, 2020
7a36e1e
Refactor index conversion
wfvining Jun 4, 2020
a402e20
Starting to work on tracking sunny days
wfvining Jun 4, 2020
5e139f6
Breaking down the tracking vs. fixed algorithms
wfvining Jun 5, 2020
3ec2a2d
Description of `fixed_max` parameter
wfvining Jun 5, 2020
fed91e7
Add 'Returns' section to docstirng
wfvining Jun 5, 2020
61b4dc3
Rough implementation of the separate tracking and fixed function
wfvining Jun 5, 2020
a9ed5ca
Set fill_value for reindex operation
wfvining Jun 5, 2020
f38d601
Make times to include in quadratic fit a parameter
wfvining Jun 5, 2020
d85d48d
First working implementation of tracking/fixed detection
wfvining Jun 8, 2020
9c0686a
Rename variables to avoid shadowing
wfvining Jun 8, 2020
f88531c
Simplify application of curve fitting functions
wfvining Jun 9, 2020
cb90f91
Remove power_min parameter
wfvining Jun 9, 2020
5fb6d82
Add attribution
wfvining Jun 9, 2020
f7af402
Only include daytime power in the positive mean.
wfvining Jun 9, 2020
d18abca
Speed up tests
wfvining Jun 9, 2020
5c796f0
Add peak_min parameter
wfvining Jun 11, 2020
9f531eb
Better documentation of internal functions
wfvining Jun 11, 2020
9677678
Adjust perturbed GHI test to accound for 1 hour timestamp spacing
wfvining Jun 11, 2020
ae90df8
Add test for a tracking system with one day of bad data
wfvining Jun 11, 2020
06d8669
Add a test for internal fitting functions
wfvining Jun 11, 2020
8c39ee9
Documentation and comment changes
wfvining Jun 12, 2020
2799297
Shorten the brief description of the 'tracking' funciton
wfvining Jun 12, 2020
ee1dad6
Use simplified_solis clearsky model
wfvining Jun 12, 2020
0d5d78e
bump pytest version for travis builds
wfvining Jun 12, 2020
61f134f
Add _nrel suffix to make room for other methods
wfvining Jun 12, 2020
c39cd39
Rename _fit.quartic to highlight that it is limited
wfvining Jun 12, 2020
90f290d
use boolean mask to select mid-day times instead of time-stamps
wfvining Jun 12, 2020
3d5bff1
Add description of the necessary conditions to mark a day 'tracking'
wfvining Jun 12, 2020
c53ddb6
Draft adding orientation functions to documentation.
wfvining Jun 12, 2020
7fde4e8
Documentation edits
wfvining Jun 12, 2020
6cd07c7
Documentation improvements from code review
wfvining Jun 15, 2020
b7414e2
Re-fill docstrings to shorten lines
wfvining Jun 15, 2020
4193c28
Rename parameters related to r-squared
wfvining Jun 15, 2020
a0d7f34
Improve description of r2_fixed_max parameter
wfvining Jun 15, 2020
369bbd2
rename midday parameter to 'quadratic_mask'
wfvining Jun 16, 2020
10d5dbb
Update description of `daytime` parameter in fixed_nrel
wfvining Jun 18, 2020
6ff5ac5
Expaning overview of features.orientation functions in API docs
wfvining Jun 18, 2020
e14e7e8
Add a test for data simulating a stuck tracker.
wfvining Jun 18, 2020
7f31f59
Move group-by-day function into util
wfvining Jun 16, 2020
4c1bbb7
Change minmum r2 default for tracking to 0.915
wfvining Jul 7, 2020
98896f6
Apply documentation edits from code review
wfvining Jul 7, 2020
6996c5c
Re-fill docstrings
wfvining Jul 9, 2020
e3b212f
Remove unnecessary paragraph from API docs
wfvining Jul 9, 2020
6d645ed
Move QCRad reference back to the Quality section
wfvining Jul 9, 2020
226f072
Add init file for util sub-package
wfvining Jul 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ python:
- "3.8"

install:
- pip install pytest==5.4.3 pytest-cov coveralls
- pip install -r requirements.txt
- pip install pytest pytest-cov coveralls

script:
- pytest --cov=pvanalytics --cov-report term-missing
Expand Down
31 changes: 27 additions & 4 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ Quality checks for weather data.
quality.weather.temperature_limits
quality.weather.wind_limits

.. rubric:: References

.. [1] C. N. Long and Y. Shi, An Automated Quality Assessment and Control
Algorithm for Surface Radiation Measurements, The Open Atmospheric
Science Journal 2, pp. 23-37, 2008.

Features
========

Expand All @@ -141,8 +147,25 @@ Clearsky

features.clearsky.reno

.. rubric:: References
Orientation
-----------

.. [1] C. N. Long and Y. Shi, An Automated Quality Assessment and Control
Algorithm for Surface Radiation Measurements, The Open Atmospheric
Science Journal 2, pp. 23-37, 2008.
System orientation refers to mounting type (fixed or tracker) and the
azimuth and tilt of the mounting. A system's orientation can be
determined by examining power or POA irradiance on days that are
relatively sunny.

This module provides functions that operate on power or POA irradiance
to identify system orientation on a daily basis. These functions can
tell you whether a day's profile matches that of a fixed system or
system with a single-axis tracker.

Care should be taken when interpreting function output since
other factors such as malfunctioning trackers can interfere with
identification.

.. autosummary::
:toctree: generated/

features.orientation.fixed_nrel
features.orientation.tracking_nrel
191 changes: 191 additions & 0 deletions pvanalytics/features/orientation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
"""Functions for identifying system orientation."""
import pandas as pd
from pvanalytics.util import _fit, _group


def _conditional_fit(day, fitfunc, freq, default=0.0, min_hours=0.0,
peak_min=None):
# If there are at least `min_hours` of data in `day` and the
# maximum for the day is greater than `peak_min` then `fitfunc` is
# applied to fit a curve to the data. `fitfunc` must be a function
# that takes a Series and returns the :math:`r^2` for a curve fit.
# If the two conditions are not met then `default` is returned and
# no curve fitting is performed.
high_enough = True
if peak_min is not None:
high_enough = day.max() > peak_min
if (_hours(day, freq) > min_hours) and high_enough:
return fitfunc(day)
return default


def _freqstr_to_hours(freq):
# Convert pandas freqstr to hours (as a float)
if freq.isalpha():
freq = '1' + freq
return pd.to_timedelta(freq).seconds / 60


def _hours(data, freq):
# Return the number of hours in `data` with timestamp
# spacing given by `freq`.
return data.count() * _freqstr_to_hours(freq)


def tracking_nrel(power_or_irradiance, daytime, r2_min=0.915,
r2_fixed_max=0.96, min_hours=5, peak_min=None,
quadratic_mask=None):
"""Flag days that match the profile of a single-axis tracking PV system
on a sunny day.

This algorithm relies on the observation that the power profile of
a single-axis tracking PV system tends to resemble a quartic
polynomial on a sunny day, I.e., two peaks are observed, one
before and one after the sun crosses the tracker azimuth. By
contrast, the power profile for a fixed tilt PV system often
resembles a quadratic polynomial on a sunny day, with a single
peak when the sun is near the system azimuth.

The algorithm fits both a quartic and a quadratic polynomial to
each day's data. A day is marked True if the quartic fit has a
sufficiently high :math:`r^2` and the quadratic fit has a
sufficiently low :math:`r^2`. Specifically, a day is marked True
when three conditions are met:

1. a restricted quartic [#]_ must fit the data with :math:`r^2`
greater than `r2_min`
2. the :math:`r^2` for the restricted quartic fit must be greater
than the :math:`r^2` for a quadratic fit
3. the :math:`r^2` for a quadratic fit must be less than
`r2_fixed_max`

Values on days where any one of these conditions is not met are
marked False.

.. [#] The specific quartic used for this fit is centered within
70 minutes of 12:00, the y-value at the center must be within
15% of the median for the day, and it must open downwards.

Parameters
----------
power_or_irradiance : Series
Timezone localized series of power or irradiance measurements.
daytime : Series
Boolean series with True for times that are during the
day. For best results this mask should exclude early morning
and late afternoon as well as night. Data at these times may have
problems with shadows that interfere with curve fitting.
r2_min : float, default 0.915
Minimum :math:`r^2` of a quartic fit for a day to be marked True.
r2_fixed_max : float, default 0.96
If the :math:`r^2` of a quadratic fit exceeds
`r2_fixed_max`, then tracking/fixed cannot be distinguished
and the day is marked False.
min_hours : float, default 5.0
Minimum number of hours with data to attempt a fit on a day.
peak_min : float, default None
The maximum `power_or_irradiance` value for a day must be
greater than `peak_min` for a fit to be attempted. If the
maximum for a day is less than `peak_min` then the day will be
marked False.
quadratic_mask : Series, default None
If None then `daytime` is used. This Series is used to remove
morning and afternoon times from the data before applying a
quadratic fit. The mask should
typically exclude more data than `daytime` in order to
eliminate long tails in the morning or afternoon that can
appear if a tracker is stuck in a West or East orientation.

Returns
-------
Series
Boolean series with True for every value on a day that has a
tracking profile (see criteria above).

Notes
-----
This algorithm is based on the PVFleets QA Analysis
project. Copyright (c) 2020 Alliance for Sustainable Energy, LLC.

"""
if quadratic_mask is None:
quadratic_mask = daytime
freq = pd.infer_freq(power_or_irradiance.index)
daily_data = _group.by_day(power_or_irradiance[daytime])
tracking_days = daily_data.apply(
_conditional_fit,
_fit.quartic_restricted,
freq=freq,
min_hours=min_hours,
peak_min=peak_min
)
fixed_days = _group.by_day(power_or_irradiance[quadratic_mask]).apply(
_conditional_fit,
_fit.quadratic,
freq=freq,
min_hours=min_hours,
peak_min=peak_min
)
return (
(tracking_days > r2_min)
& (tracking_days > fixed_days)
& (fixed_days < r2_fixed_max)
).reindex(power_or_irradiance.index, method='pad', fill_value=False)


def fixed_nrel(power_or_irradiance, daytime, r2_min=0.94,
min_hours=5, peak_min=None):
"""Flag days that match the profile of a fixed PV system on a sunny day.

This algorithm relies on the observation that the power profile of a
fixed tilt PV system often resembles a quadratic polynomial on a
sunny day, with a single peak when the sun is near the system azimuth.

A day is marked True when the :math:`r^2` for a quadratic fit to the
power data is greater than `r2_min`.

Parameters
----------
power_or_irradiance : Series
Timezone localized series of power or irradiance measurements.
daytime : Series
Boolean series with True for times that are during the
day. For best results this mask should exclude early morning
and evening as well as night. Data at these times may have
problems with shadows that interfere with curve fitting.
r2_min : float, default 0.94
Minimum :math:`r^2` of a quadratic fit for a day to be marked True.
min_hours : float, default 5.0
Minimum number of hours with data to attempt a fit on a day.
peak_min : float, default None
The maximum `power_or_irradiance` value for a day must be
greater than `peak_min` for a fit to be attempted. If the
maximum for a day is less than `peak_min` then the day will be
marked False.

Returns
-------
Series
True for values on days where `power_or_irradiance` matches
the expected parabolic profile for a fixed PV system on a sunny day.

Notes
-----
This algorithm is based on the PVFleets QA Analysis
project. Copyright (c) 2020 Alliance for Sustainable Energy, LLC.

"""
freq = pd.infer_freq(power_or_irradiance.index)
daily_data = _group.by_day(
power_or_irradiance[daytime]
)
fixed_days = daily_data.apply(
_conditional_fit,
_fit.quadratic,
freq=freq,
min_hours=min_hours,
peak_min=peak_min
)
return (
fixed_days > r2_min
).reindex(power_or_irradiance.index, method='pad', fill_value=False)
File renamed without changes.
Loading