-
Notifications
You must be signed in to change notification settings - Fork 21
Description
The NIGHTTIME flag is set based on the solar zenith at the interval label timestamp. For example, for an observation with interval label = beginning, for an interval [start, end)
the flag is computed based on only solar_zenith(start) < 87
. And for an observation with interval label = ending, for an interval (start, end]
the flag is computed based on only solar_zenith(end) < 87
. This is fine for 1 minute observations, but becomes problematic for longer intervals. We instead should label intervals by the fraction of time within an interval that is nighttime.
Here are the relevant sections of code:
solarforecastarbiter-core/solarforecastarbiter/validation/validator.py
Lines 559 to 575 in 58944fe
@mask_flags('NIGHTTIME') | |
def check_irradiance_day_night(solar_zenith, max_zenith=87): | |
""" Checks for day/night periods based on solar zenith. | |
Parameters | |
---------- | |
solar_zenith : Series | |
Solar zenith angle in degrees | |
max_zenith : maximum zenith angle for a daylight time | |
Returns | |
------- | |
flags : Series | |
True when solar zenith is less than max_zenith. | |
""" | |
flags = _check_limits(solar_zenith, ub=max_zenith) | |
return flags |
solarforecastarbiter-core/solarforecastarbiter/validation/tasks.py
Lines 31 to 37 in ea5d701
def _solpos_night(observation, values): | |
solar_position = pvmodel.calculate_solar_position( | |
observation.site.latitude, observation.site.longitude, | |
observation.site.elevation, values.index) | |
night_flag = validator.check_irradiance_day_night(solar_position['zenith'], | |
_return_mask=True) | |
return solar_position, night_flag |
solarforecastarbiter-core/solarforecastarbiter/pvmodel.py
Lines 23 to 46 in 886897e
def calculate_solar_position(latitude, longitude, elevation, times): | |
""" | |
Calculates solar position using pvlib's implementation of NREL SPA. | |
Parameters | |
---------- | |
latitude : float | |
longitude : float | |
elevation : float | |
times : pd.DatetimeIndex | |
Returns | |
------- | |
solar_position : pd.DataFrame | |
The DataFrame will have the following columns: apparent_zenith | |
(degrees), zenith (degrees), apparent_elevation (degrees), | |
elevation (degrees), azimuth (degrees), | |
equation_of_time (minutes). | |
""" | |
solpos = pvlib.solarposition.get_solarposition(times, latitude, | |
longitude, | |
altitude=elevation, | |
method='nrel_numpy') | |
return solpos |
My proposal for the arbiter is the following:
tasks._solpos_night
computes solar position for sub-intervals, similar topersistence_scalar_index
. We'd need to choose an appropriate sub-interval length. 1 minute? 5 minutes like inpersistence_scalar_index
?validator.check_irradiance_day_night
computes the flag on the higher resolution solar position data.tasks._solpos_night
applies boolean resampling logic, similar to accounting for marginal nighttime and clearsky conditions when resampling observations #556, to create a flag for the times in the original index.- One of the following:
A. Resample the solar position data to match the observation rules, and return.
B. Subset and continue to return the instantaneous values at the labels.
C. Return the full high resolution solar position dataframe and modify the downstream functions to account for averaging/subsetting. Perhaps my favorite approach, but also the most work. - Return the
solar_position, night_flag
tuple.
@cwhanse have you looked into this for pvanalytics? I didn't see any code or issues.