-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
A recent paper describes improvements on pvlib.clearsky.detect_clearsky
. I've got a port of their Matlab code to a python function detect_clearsky_brightsun
, and trying to keep the interface common for the two detect_clearsky functions.
Although the BrightSun method uses the similar statistics to determine clearsky times, the limits aren't constants - they are functions of zenith. And the method needs additional parameters for the duration filters and the optimization of the clear-sky model. The duration filters are different in the ~hour after sunrise/before sunset than in midday.
I have the limit and control arguments coded as dicts and provide constant dicts with the default values (which needs to change, since we don't want mutable defaults).
For the solar position related inputs (zenith and sunrise/sunset times), several options:
- pass in a
pvlib.location.Location
and calculate solar position and sunrise/set times internal todetect_clearsky_brightsun
. This is what I have done in the draft function, it makes the code straightforward but at the expense of a less-than-explicit interface. - pass
zenith
,sunrise
andsunset
as separate arguments. This seems awkward and somewhat risky, since calculating sunrise and sunset times withpvlib.solarposition.rise_set_transit_xxx
can have different behaviors (e.g., using_spa
the first sunset time is after the first sunrise time, even if the data start at midday,_ephem
has a kwarg that controls this behavior and returns the first sunset after the first data point). - pass
zenith
only and a kwarg for a zenith angle that defines sunrise and sunset, rather than asking for sunrise/sunset as input or using thesolarposition
functions.
Interested in feedback about this approach before I open a PR.
Current detect_clearsky
function:
def detect_clearsky(measured, clearsky, times, window_length,
mean_diff=75, max_diff=75,
lower_line_length=-5, upper_line_length=10,
var_diff=0.005, slope_dev=8, max_iterations=20,
return_components=False):
Draft new function and dicts:
# constants used for BrightSun algorithm
# dict of functions that return limits for each criteria for BrightSun
BRIGHT_SUN_LIMITS = {
'mean_diff': _mean_max_lim,
'max_diff': _mean_max_lim,
'line_length': _line_length_lim,
'slope_nstd': lambda x: 0.4,
'slope_diff': _slope_diff_lim}
# dict of parameters for daily clear-sky model optimization for BrightSun
BRIGHT_SUN_OPTIM = {
'min_ghi': 30, 'min_pts_per_day': 60, 'alpha_limits': (0.7, 1.5),
'maxiter': 20}
# dict of parameters for duration criteria for BrightSun
BRIGHT_SUN_FILTERS = {'long_window': 90, 'long_fraction': (90 - 80) / 90.,
'separation_window': 30,
'rise_set_period': 90, 'rise_set_window': 10,
'rise_set_fraction': (10 - 2) / 10}
def detect_clearsky_brightsun(measured, clearsky, times,
location,
window_length,
limits=BRIGHT_SUN_LIMITS,
optim=BRIGHT_SUN_OPTIM,
filters=BRIGHT_SUN_FILTERS,
return_components=False):
The values for the BRIGHT_SUN_LIMITS
dict are functions:
def _mean_max_lim(x):
# limit for criteria 1 and 2 of BrightSun algorithm
return np.piecewise(
x, condlist=[x < 20, (x >= 20) & (x < 30), (x >= 30) & (x < 90),
x >= 90],
funclist=[0.25, lambda x: np.interp(x, xp=[20, 30], fp=[0.25, 0.125]),
lambda x: np.interp(x, xp=[20, 30], fp=[0.125, 0.5]), 0.5])
def _line_length_lim(x):
# for criteria 3 of BrightSun algorithm
# limit at zenith >= 30 is 0.5 per email with Jaime Bright and Matlab code
# J. Bright states he believes the value of 2 in the 2020 paper is
# incorrect
return np.piecewise(x, condlist=[x < 30, x >= 30],
funclist=[lambda x: np.interp(x, xp=[0, 30], fp=[7, 0.5]), 0.5])
def _slope_diff_lim(x):
# for criteria 5 of BrightSun algorithm
return np.piecewise(x, condlist=[x < 30, x >= 30],
funclist=[lambda x: np.interp(x, xp=[0, 30], fp=[45, 15]), 15])