|
| 1 | +""" |
| 2 | +Diffuse Fraction Estimation |
| 3 | +=========================== |
| 4 | +
|
| 5 | +Comparison of diffuse fraction estimation methods used to derive direct and |
| 6 | +diffuse components from measured global horizontal irradiance. |
| 7 | +""" |
| 8 | + |
| 9 | +# %% |
| 10 | +# This example demonstrates how to use diffuse fraction estimation methods to |
| 11 | +# obtain direct and diffuse components from measured global horizontal |
| 12 | +# irradiance (GHI). Irradiance sensors such as pyranometers typically only |
| 13 | +# measure GHI. pvlib provides several functions that can be used to separate |
| 14 | +# GHI into the diffuse and direct components. The separate components are |
| 15 | +# needed to estimate the total irradiance on a tilted surface. |
| 16 | + |
| 17 | +import pathlib |
| 18 | +from matplotlib import pyplot as plt |
| 19 | +import pandas as pd |
| 20 | +from pvlib.iotools import read_tmy3 |
| 21 | +from pvlib.solarposition import get_solarposition |
| 22 | +from pvlib import irradiance |
| 23 | +import pvlib |
| 24 | + |
| 25 | +# For this example we use the Greensboro, North Carolina, TMY3 file which is |
| 26 | +# in the pvlib data directory. TMY3 are made from the median months from years |
| 27 | +# of data measured from 1990 to 2010. Therefore we change the timestamps to a |
| 28 | +# common year, 1990. |
| 29 | +DATA_DIR = pathlib.Path(pvlib.__file__).parent / 'data' |
| 30 | +greensboro, metadata = read_tmy3(DATA_DIR / '723170TYA.CSV', coerce_year=1990) |
| 31 | + |
| 32 | +# Many of the diffuse fraction estimation methods require the "true" zenith, so |
| 33 | +# we calculate the solar positions for the 1990 at Greensboro, NC. |
| 34 | +# NOTE: TMY3 files timestamps indicate the end of the hour, so shift indices |
| 35 | +# back 30-minutes to calculate solar position at center of the interval |
| 36 | +solpos = get_solarposition( |
| 37 | + greensboro.index.shift(freq="-30T"), latitude=metadata['latitude'], |
| 38 | + longitude=metadata['longitude'], altitude=metadata['altitude'], |
| 39 | + pressure=greensboro.Pressure*100, # convert from millibar to Pa |
| 40 | + temperature=greensboro.DryBulb) |
| 41 | +solpos.index = greensboro.index # reset index to end of the hour |
| 42 | + |
| 43 | +# %% |
| 44 | +# pvlib Decomposition Functions |
| 45 | +# ----------------------------- |
| 46 | +# Methods for separating DHI into diffuse and direct components include: |
| 47 | +# `DISC`_, `DIRINT`_, `Erbs`_, and `Boland`_. |
| 48 | + |
| 49 | +# %% |
| 50 | +# DISC |
| 51 | +# ---- |
| 52 | +# |
| 53 | +# DISC :py:func:`~pvlib.irradiance.disc` is an empirical correlation developed |
| 54 | +# at SERI (now NREL) in 1987. The direct normal irradiance (DNI) is related to |
| 55 | +# clearness index (kt) by two polynomials split at kt = 0.6, then combined with |
| 56 | +# an exponential relation with airmass. |
| 57 | + |
| 58 | +out_disc = irradiance.disc( |
| 59 | + greensboro.GHI, solpos.zenith, greensboro.index, greensboro.Pressure*100) |
| 60 | +# use "complete sum" AKA "closure" equations: DHI = GHI - DNI * cos(zenith) |
| 61 | +df_disc = irradiance.complete_irradiance( |
| 62 | + solar_zenith=solpos.apparent_zenith, ghi=greensboro.GHI, dni=out_disc.dni, |
| 63 | + dhi=None) |
| 64 | +out_disc = out_disc.rename(columns={'dni': 'dni_disc'}) |
| 65 | +out_disc['dhi_disc'] = df_disc.dhi |
| 66 | + |
| 67 | +# %% |
| 68 | +# DIRINT |
| 69 | +# ------ |
| 70 | +# |
| 71 | +# DIRINT :py:func:`~pvlib.irradiance.dirint` is a modification of DISC |
| 72 | +# developed by Richard Perez and Pierre Ineichen in 1992. |
| 73 | + |
| 74 | +dni_dirint = irradiance.dirint( |
| 75 | + greensboro.GHI, solpos.zenith, greensboro.index, greensboro.Pressure*100, |
| 76 | + temp_dew=greensboro.DewPoint) |
| 77 | +# use "complete sum" AKA "closure" equation: DHI = GHI - DNI * cos(zenith) |
| 78 | +df_dirint = irradiance.complete_irradiance( |
| 79 | + solar_zenith=solpos.apparent_zenith, ghi=greensboro.GHI, dni=dni_dirint, |
| 80 | + dhi=None) |
| 81 | +out_dirint = pd.DataFrame( |
| 82 | + {'dni_dirint': dni_dirint, 'dhi_dirint': df_dirint.dhi}, |
| 83 | + index=greensboro.index) |
| 84 | + |
| 85 | +# %% |
| 86 | +# Erbs |
| 87 | +# ---- |
| 88 | +# |
| 89 | +# The Erbs method, :py:func:`~pvlib.irradiance.erbs` developed by Daryl Gregory |
| 90 | +# Erbs at the University of Wisconsin in 1982 is a piecewise correlation that |
| 91 | +# splits kt into 3 regions: linear for kt <= 0.22, a 4th order polynomial |
| 92 | +# between 0.22 < kt <= 0.8, and a horizontal line for kt > 0.8. |
| 93 | + |
| 94 | +out_erbs = irradiance.erbs(greensboro.GHI, solpos.zenith, greensboro.index) |
| 95 | +out_erbs = out_erbs.rename(columns={'dni': 'dni_erbs', 'dhi': 'dhi_erbs'}) |
| 96 | + |
| 97 | +# %% |
| 98 | +# Boland |
| 99 | +# ------ |
| 100 | +# |
| 101 | +# The Boland method, :py:func:`~pvlib.irradiance.boland` is a single logistic |
| 102 | +# exponential correlation that is continuously differentiable and bounded |
| 103 | +# between zero and one. |
| 104 | + |
| 105 | +out_boland = irradiance.boland(greensboro.GHI, solpos.zenith, greensboro.index) |
| 106 | +out_boland = out_boland.rename( |
| 107 | + columns={'dni': 'dni_boland', 'dhi': 'dhi_boland'}) |
| 108 | + |
| 109 | +# %% |
| 110 | +# Comparison Plots |
| 111 | +# ---------------- |
| 112 | +# In the plots below we compare the four decomposition models to the TMY3 file |
| 113 | +# for Greensboro, North Carolina. We also compare the clearness index, kt, with |
| 114 | +# GHI normalized by a reference irradiance, E0 = 1000 [W/m^2], to highlight |
| 115 | +# spikes caused when cosine of zenith approaches zero, particularly at sunset. |
| 116 | +# |
| 117 | +# First we combine the dataframes for the decomposition models and the TMY3 |
| 118 | +# file together to make plotting easier. |
| 119 | + |
| 120 | +dni_renames = { |
| 121 | + 'DNI': 'TMY3', 'dni_disc': 'DISC', 'dni_dirint': 'DIRINT', |
| 122 | + 'dni_erbs': 'Erbs', 'dni_boland': 'Boland'} |
| 123 | +dni = [ |
| 124 | + greensboro.DNI, out_disc.dni_disc, out_dirint.dni_dirint, |
| 125 | + out_erbs.dni_erbs, out_boland.dni_boland] |
| 126 | +dni = pd.concat(dni, axis=1).rename(columns=dni_renames) |
| 127 | +dhi_renames = { |
| 128 | + 'DHI': 'TMY3', 'dhi_disc': 'DISC', 'dhi_dirint': 'DIRINT', |
| 129 | + 'dhi_erbs': 'Erbs', 'dhi_boland': 'Boland'} |
| 130 | +dhi = [ |
| 131 | + greensboro.DHI, out_disc.dhi_disc, out_dirint.dhi_dirint, |
| 132 | + out_erbs.dhi_erbs, out_boland.dhi_boland] |
| 133 | +dhi = pd.concat(dhi, axis=1).rename(columns=dhi_renames) |
| 134 | +ghi_kt = pd.concat([greensboro.GHI/1000.0, out_erbs.kt], axis=1) |
| 135 | + |
| 136 | +# %% |
| 137 | +# Winter |
| 138 | +# ++++++ |
| 139 | +# Finally, let's plot them for a few winter days and compare |
| 140 | + |
| 141 | +JAN04, JAN07 = '1990-01-04 00:00:00-05:00', '1990-01-07 23:59:59-05:00' |
| 142 | +f, ax = plt.subplots(3, 1, figsize=(8, 10), sharex=True) |
| 143 | +dni[JAN04:JAN07].plot(ax=ax[0]) |
| 144 | +ax[0].grid(which="both") |
| 145 | +ax[0].set_ylabel('DNI $[W/m^2]$') |
| 146 | +ax[0].set_title('Comparison of Diffuse Fraction Estimation Methods') |
| 147 | +dhi[JAN04:JAN07].plot(ax=ax[1]) |
| 148 | +ax[1].grid(which="both") |
| 149 | +ax[1].set_ylabel('DHI $[W/m^2]$') |
| 150 | +ghi_kt[JAN04:JAN07].plot(ax=ax[2]) |
| 151 | +ax[2].grid(which='both') |
| 152 | +ax[2].set_ylabel(r'$\frac{GHI}{E0}, k_t$') |
| 153 | +f.tight_layout() |
| 154 | + |
| 155 | +# %% |
| 156 | +# Spring |
| 157 | +# ++++++ |
| 158 | +# And a few spring days ... |
| 159 | + |
| 160 | +APR04, APR07 = '1990-04-04 00:00:00-05:00', '1990-04-07 23:59:59-05:00' |
| 161 | +f, ax = plt.subplots(3, 1, figsize=(8, 10), sharex=True) |
| 162 | +dni[APR04:APR07].plot(ax=ax[0]) |
| 163 | +ax[0].grid(which="both") |
| 164 | +ax[0].set_ylabel('DNI $[W/m^2]$') |
| 165 | +ax[0].set_title('Comparison of Diffuse Fraction Estimation Methods') |
| 166 | +dhi[APR04:APR07].plot(ax=ax[1]) |
| 167 | +ax[1].grid(which="both") |
| 168 | +ax[1].set_ylabel('DHI $[W/m^2]$') |
| 169 | +ghi_kt[APR04:APR07].plot(ax=ax[2]) |
| 170 | +ax[2].grid(which='both') |
| 171 | +ax[2].set_ylabel(r'$\frac{GHI}{E0}, k_t$') |
| 172 | +f.tight_layout() |
| 173 | + |
| 174 | +# %% |
| 175 | +# Summer |
| 176 | +# ++++++ |
| 177 | +# And few summer days to finish off the seasons. |
| 178 | + |
| 179 | +JUL04, JUL07 = '1990-07-04 00:00:00-05:00', '1990-07-07 23:59:59-05:00' |
| 180 | +f, ax = plt.subplots(3, 1, figsize=(8, 10), sharex=True) |
| 181 | +dni[JUL04:JUL07].plot(ax=ax[0]) |
| 182 | +ax[0].grid(which="both") |
| 183 | +ax[0].set_ylabel('DNI $[W/m^2]$') |
| 184 | +ax[0].set_title('Comparison of Diffuse Fraction Estimation Methods') |
| 185 | +dhi[JUL04:JUL07].plot(ax=ax[1]) |
| 186 | +ax[1].grid(which="both") |
| 187 | +ax[1].set_ylabel('DHI $[W/m^2]$') |
| 188 | +ghi_kt[JUL04:JUL07].plot(ax=ax[2]) |
| 189 | +ax[2].grid(which='both') |
| 190 | +ax[2].set_ylabel(r'$\frac{GHI}{E0}, k_t$') |
| 191 | +f.tight_layout() |
| 192 | + |
| 193 | +# %% |
| 194 | +# Conclusion |
| 195 | +# ---------- |
| 196 | +# This example compares several decomposition models to a TMY3 file for |
| 197 | +# Greensboro, North Carolina. However, DNI and DHI in TMY3 files are themselves |
| 198 | +# the output of models (either METSTAT or SUNY), and so differences between |
| 199 | +# *e.g.* DISC output and the TMY3 file shouldn't be regarded as errors, and |
| 200 | +# it's not a reasonable expectation to assume that the four models should |
| 201 | +# reproduce the TMY3 values. We refer those interested to the `TMY3`_ and |
| 202 | +# `NSRDB`_ user manuals. |
| 203 | +# |
| 204 | +# The Erbs and Boland models are correlations only based on the clearness index |
| 205 | +# kt, which is the ratio of GHI to the the horizontal component of the |
| 206 | +# extra-terrestrial irradiance. At low sun elevation (zenith near 90 degrees), |
| 207 | +# especially near sunset, kt can explode because the denominator |
| 208 | +# (extra-terrestrial irradiance) approaches zero. In pvlib this behavior is |
| 209 | +# moderated by ``min_cos_zenith`` and ``max_clearness_index`` which each have |
| 210 | +# reasonable defaults. Even so, near sunset there are still spikes in kt and |
| 211 | +# DNI from Erbs and Boland for Jan. 5th & 7th, April 4th, 5th, & 7th, and July |
| 212 | +# 6th & 7th. |
| 213 | +# |
| 214 | +# By contrast, the DISC and DIRINT methods estimate DNI first by means of |
| 215 | +# correlations, which include additional variables such as airmass. These |
| 216 | +# methods seem to reduce DNI spikes over 1000 [W/m^2]. |
| 217 | +# |
| 218 | +# .. _TMY3: https://www.nrel.gov/docs/fy08osti/43156.pdf |
| 219 | +# .. _NSRDB: https://www.nrel.gov/docs/fy12osti/54824.pdf |
0 commit comments