Skip to content

nighttime flag should account for time within an interval #567

@wholmgren

Description

@wholmgren

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:

@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

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

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:

  1. tasks._solpos_night computes solar position for sub-intervals, similar to persistence_scalar_index. We'd need to choose an appropriate sub-interval length. 1 minute? 5 minutes like in persistence_scalar_index?
  2. validator.check_irradiance_day_night computes the flag on the higher resolution solar position data.
  3. 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.
  4. 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.
  5. Return the solar_position, night_flag tuple.

@cwhanse have you looked into this for pvanalytics? I didn't see any code or issues.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions