From 936439c44cad5a5e9bd1622c8325b69b93f49ce6 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Wed, 30 Nov 2022 11:23:42 +0100 Subject: [PATCH 1/4] Create nighttime_offset_correction function --- pvanalytics/features/irradiance.py | 65 ++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 pvanalytics/features/irradiance.py diff --git a/pvanalytics/features/irradiance.py b/pvanalytics/features/irradiance.py new file mode 100644 index 000000000..83c425b95 --- /dev/null +++ b/pvanalytics/features/irradiance.py @@ -0,0 +1,65 @@ +"""Quality control functions for irradiance data.""" + +import numpy as np +import datetime as dt + + +def nighttime_offset_correction(irradiance, zenith, sza_night_limit=100, + label='right', midnight_method='zenith', + aggregation_method='median'): + """ + Apply nighttime correction to irradiance time series. + + Parameters + ---------- + irradiance : pd.Series + Pandas Series of irradiance data. + zenith : pd.Series + Pandas Series of zenith angles corresponding to the irradiance time + series. + sza_night_limit : float, optional + Solar zenith angle boundary limit (periods with zenith angles greater + or equal are used to compute the nighttime offset). The default is 100. + label : {'right', 'left'}, optional + Whether the timestamps correspond to the start/left or end/right of the + interval. The default is 'right'. + midnight_method : {'zenith', 'time'}, optional + Method for determining midnight. The default is 'zenith', which + assumes midnight occurs when the zenith angle is at the maximum. + aggregation_method : {'median', 'mean'}, optional + Method for calculating nighttime offset. The default is 'median'. + + Returns + ------- + corrected_irradiance : pd.Series + Pandas Series of nighttime corrected irradiance. + """ + # Raise an error if arguments are incorrect + if label not in ['right', 'left']: + raise ValueError("label must be 'right' or 'left'.") + if aggregation_method not in ['median', 'mean']: + raise ValueError("aggregation_method must be 'mean' or 'median'.") + + # Create boolean series where nighttime is one (calculated based on the + # zenith angle) + midnight_zenith = (zenith.diff().apply(np.sign).diff() < 0) + # Assign unique number to each day + day_number_zenith = midnight_zenith.cumsum() + + # Choose grouping parameter based on the midnight_method + if midnight_method == 'zenith': + grouping_category = day_number_zenith + elif midnight_method == 'time': + grouping_category = irradiance.index.date + if label == 'right': + grouping_category[irradiance.index.time == dt.time(0)] += -dt.timedelta(days=1) + else: + raise ValueError("midnight_method must be 'zenith' or 'time'.") + + # Create Pandas Series only containing nighttime irradiance + nighttime_irradiance = irradiance[zenith >= sza_night_limit] + # Calculate nighttime offset + nighttime_offset = nighttime_irradiance.groupby(grouping_category).transform(aggregation_method) + # Calculate corrected irradiance time series + corrected_irradiance = irradiance - nighttime_offset + return corrected_irradiance From bfec1fac6b93c821c21f42548ecf8868e63bc128 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Wed, 30 Nov 2022 23:44:11 +0100 Subject: [PATCH 2/4] Correct coding error --- pvanalytics/features/irradiance.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pvanalytics/features/irradiance.py b/pvanalytics/features/irradiance.py index 83c425b95..d021d1161 100644 --- a/pvanalytics/features/irradiance.py +++ b/pvanalytics/features/irradiance.py @@ -18,14 +18,12 @@ def nighttime_offset_correction(irradiance, zenith, sza_night_limit=100, Pandas Series of zenith angles corresponding to the irradiance time series. sza_night_limit : float, optional - Solar zenith angle boundary limit (periods with zenith angles greater - or equal are used to compute the nighttime offset). The default is 100. + Solar zenith angle boundary limit. The default is 100. label : {'right', 'left'}, optional - Whether the timestamps correspond to the start/left or end/right of the - interval. The default is 'right'. + Whether the right/start or left/end of the interval is used to label + the interval. The default is 'right'. midnight_method : {'zenith', 'time'}, optional - Method for determining midnight. The default is 'zenith', which - assumes midnight occurs when the zenith angle is at the maximum. + Method for determining midnight. The default is 'zenith'. aggregation_method : {'median', 'mean'}, optional Method for calculating nighttime offset. The default is 'median'. @@ -57,9 +55,13 @@ def nighttime_offset_correction(irradiance, zenith, sza_night_limit=100, raise ValueError("midnight_method must be 'zenith' or 'time'.") # Create Pandas Series only containing nighttime irradiance - nighttime_irradiance = irradiance[zenith >= sza_night_limit] + # (daytime values are set to nan) + nighttime_irradiance = irradiance.copy() + nighttime_irradiance[zenith < sza_night_limit] = np.nan # Calculate nighttime offset nighttime_offset = nighttime_irradiance.groupby(grouping_category).transform(aggregation_method) + # In case nighttime offset cannot be determined (nan), set it to zero + nighttime_offset = nighttime_offset.fillna(0) # Calculate corrected irradiance time series corrected_irradiance = irradiance - nighttime_offset return corrected_irradiance From 54a80c10e3dc761158f74034fb8db5ee6a1dc251 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Mon, 12 Dec 2022 15:49:14 +0100 Subject: [PATCH 3/4] Implement .resample method --- pvanalytics/features/irradiance.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/pvanalytics/features/irradiance.py b/pvanalytics/features/irradiance.py index d021d1161..ab22a9dde 100644 --- a/pvanalytics/features/irradiance.py +++ b/pvanalytics/features/irradiance.py @@ -1,7 +1,6 @@ """Quality control functions for irradiance data.""" import numpy as np -import datetime as dt def nighttime_offset_correction(irradiance, zenith, sza_night_limit=100, @@ -44,22 +43,19 @@ def nighttime_offset_correction(irradiance, zenith, sza_night_limit=100, # Assign unique number to each day day_number_zenith = midnight_zenith.cumsum() - # Choose grouping parameter based on the midnight_method + # Create Pandas Series only containing nighttime irradiance + # (daytime values are set to nan) + nighttime_irradiance = irradiance.copy() + nighttime_irradiance[zenith < sza_night_limit] = np.nan + + # Calculate nighttime offset (method depends on midnight_method) if midnight_method == 'zenith': - grouping_category = day_number_zenith + nighttime_offset = nighttime_irradiance.groupby(day_number_zenith).transform(aggregation_method) elif midnight_method == 'time': - grouping_category = irradiance.index.date - if label == 'right': - grouping_category[irradiance.index.time == dt.time(0)] += -dt.timedelta(days=1) + nighttime_offset = nighttime_irradiance.resample('1d', label=label, closed=label).transform(aggregation_method) else: raise ValueError("midnight_method must be 'zenith' or 'time'.") - # Create Pandas Series only containing nighttime irradiance - # (daytime values are set to nan) - nighttime_irradiance = irradiance.copy() - nighttime_irradiance[zenith < sza_night_limit] = np.nan - # Calculate nighttime offset - nighttime_offset = nighttime_irradiance.groupby(grouping_category).transform(aggregation_method) # In case nighttime offset cannot be determined (nan), set it to zero nighttime_offset = nighttime_offset.fillna(0) # Calculate corrected irradiance time series From 67c87fc11936cc9ad5255c8acd095eea6e115b6e Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Thu, 1 Jun 2023 20:37:03 +0200 Subject: [PATCH 4/4] Add na_value parameer --- pvanalytics/features/irradiance.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pvanalytics/features/irradiance.py b/pvanalytics/features/irradiance.py index ab22a9dde..f88ddd4b5 100644 --- a/pvanalytics/features/irradiance.py +++ b/pvanalytics/features/irradiance.py @@ -5,15 +5,15 @@ def nighttime_offset_correction(irradiance, zenith, sza_night_limit=100, label='right', midnight_method='zenith', - aggregation_method='median'): + aggregation_method='median', na_value=0): """ - Apply nighttime correction to irradiance time series. + Apply nighttime offset correction to irradiance time series. Parameters ---------- - irradiance : pd.Series + irradiance : Series Pandas Series of irradiance data. - zenith : pd.Series + zenith : Series Pandas Series of zenith angles corresponding to the irradiance time series. sza_night_limit : float, optional @@ -22,14 +22,17 @@ def nighttime_offset_correction(irradiance, zenith, sza_night_limit=100, Whether the right/start or left/end of the interval is used to label the interval. The default is 'right'. midnight_method : {'zenith', 'time'}, optional - Method for determining midnight. The default is 'zenith'. + Method for determining midnight. The default is 'zenith', which assumes + midnight corresponds to when zenith angle is at its maximum. aggregation_method : {'median', 'mean'}, optional Method for calculating nighttime offset. The default is 'median'. + fillna : float, optional + Offset correction to apply if offset cannot be determined. Returns ------- - corrected_irradiance : pd.Series - Pandas Series of nighttime corrected irradiance. + corrected_irradiance : Series + Nighttime corrected irradiance. """ # Raise an error if arguments are incorrect if label not in ['right', 'left']: @@ -50,14 +53,14 @@ def nighttime_offset_correction(irradiance, zenith, sza_night_limit=100, # Calculate nighttime offset (method depends on midnight_method) if midnight_method == 'zenith': - nighttime_offset = nighttime_irradiance.groupby(day_number_zenith).transform(aggregation_method) + nighttime_offset = nighttime_irradiance.groupby(day_number_zenith).transform(aggregation_method) # noqa:E501 elif midnight_method == 'time': - nighttime_offset = nighttime_irradiance.resample('1d', label=label, closed=label).transform(aggregation_method) + nighttime_offset = nighttime_irradiance.resample('1d', label=label, closed=label).transform(aggregation_method) # noqa:E501 else: raise ValueError("midnight_method must be 'zenith' or 'time'.") # In case nighttime offset cannot be determined (nan), set it to zero - nighttime_offset = nighttime_offset.fillna(0) + nighttime_offset = nighttime_offset.fillna(na_value) # Calculate corrected irradiance time series corrected_irradiance = irradiance - nighttime_offset return corrected_irradiance