|
| 1 | +""" |
| 2 | +Spectral Mismatch Estimation |
| 3 | +============================ |
| 4 | +
|
| 5 | +Comparison of methods to estimate the spectral mismatch factor |
| 6 | +from atmospheric variable inputs. |
| 7 | +""" |
| 8 | + |
| 9 | +# %% |
| 10 | +# Introduction |
| 11 | +# ------------ |
| 12 | +# This example demonstrates how to use different ``spectrum.spectral_factor_*`` |
| 13 | +# models in pvlib-python to calculate the spectral mismatch factor, :math:`M`. |
| 14 | +# While :math:`M` for a photovoltaic (PV) module can be calculated exactly |
| 15 | +# using spectral irradiance and module spectral response data, these data are |
| 16 | +# often not available. pvlib-python provides several functions to estimate the |
| 17 | +# spectral mismatch factor, :math:`M`, using proxies of the prevailing spectral |
| 18 | +# irradiance conditions, such as air mass and clearsky index, which are easily |
| 19 | +# derived from common ground-based measurements such as broadband irradiance. |
| 20 | +# More information on a range of spectral factor models, as well as the |
| 21 | +# variables upon which they are based, can be found in [1]_. |
| 22 | +# |
| 23 | +# To demonstrate this functionality, first we need to import some data. |
| 24 | +# This example uses a Typical Meteorological Year 3 |
| 25 | +# (TMY3) file for the location of Greensboro, North Carolina, from the |
| 26 | +# pvlib-python data directory. This TMY3 file is constructed using the median |
| 27 | +# month from each year between 1980 and 2003, from which we extract the first |
| 28 | +# week of August 2001 to analyse. |
| 29 | + |
| 30 | +# %% |
| 31 | +import pathlib |
| 32 | +from matplotlib import pyplot as plt |
| 33 | +import pandas as pd |
| 34 | +import pvlib |
| 35 | +from pvlib import location |
| 36 | + |
| 37 | +DATA_DIR = pathlib.Path(pvlib.__file__).parent / 'data' |
| 38 | +meteo, metadata = pvlib.iotools.read_tmy3(DATA_DIR / '723170TYA.CSV', |
| 39 | + coerce_year=2001, map_variables=True) |
| 40 | +meteo = meteo.loc['2001-08-01':'2001-08-07'] |
| 41 | + |
| 42 | +# %% |
| 43 | +# Spectral Factor Functions |
| 44 | +# ------------------------- |
| 45 | +# This example demonstrates the application of three pvlib-python |
| 46 | +# spectral factor functions: |
| 47 | +# |
| 48 | +# - :py:func:`~pvlib.spectrum.spectral_factor_sapm`, which requires only |
| 49 | +# the absolute airmass, :math:`AM_a` |
| 50 | +# - :py:func:`~pvlib.spectrum.spectral_factor_pvspec`, which requires |
| 51 | +# :math:`AM_a` and the clearsky index, :math:`k_c` |
| 52 | +# - :py:func:`~pvlib.spectrum.spectral_factor_firstsolar`, which requires |
| 53 | +# :math:`AM_a` and the atmospheric precipitable water content, :math:`W` |
| 54 | + |
| 55 | +# %% |
| 56 | +# Calculation of inputs |
| 57 | +# --------------------- |
| 58 | +# Let's calculate the absolute air mass, which is required for all three |
| 59 | +# models. We use the Kasten and Young [2]_ model, which requires the apparent |
| 60 | +# sun zenith. Note: TMY3 files timestamps indicate the end of the hour, so we |
| 61 | +# shift the indices back 30-minutes to calculate solar position at the centre |
| 62 | +# of the interval. |
| 63 | + |
| 64 | +# Create a location object |
| 65 | +lat, lon = metadata['latitude'], metadata['longitude'] |
| 66 | +alt = altitude = metadata['altitude'] |
| 67 | +tz = 'Etc/GMT+5' |
| 68 | +loc = location.Location(lat, lon, tz=tz, name='Greensboro, NC') |
| 69 | + |
| 70 | +# Calculate solar position parameters |
| 71 | +solpos = loc.get_solarposition( |
| 72 | + meteo.index.shift(freq="-30min"), |
| 73 | + pressure=meteo.pressure*100, # convert from millibar to Pa |
| 74 | + temperature=meteo.temp_air) |
| 75 | +solpos.index = meteo.index # reset index to end of the hour |
| 76 | + |
| 77 | +airmass_relative = pvlib.atmosphere.get_relative_airmass( |
| 78 | + solpos.apparent_zenith).dropna() |
| 79 | +airmass_absolute = pvlib.atmosphere.get_absolute_airmass(airmass_relative, |
| 80 | + meteo.pressure*100) |
| 81 | +# %% |
| 82 | +# Now we calculate the clearsky index, :math:`k_c`, which is the ratio of GHI |
| 83 | +# to clearsky GHI. |
| 84 | + |
| 85 | +cs = loc.get_clearsky(meteo.index.shift(freq="-30min")) |
| 86 | +cs.index = meteo.index # reset index to end of the hour |
| 87 | +kc = pvlib.irradiance.clearsky_index(meteo.ghi, cs.ghi) |
| 88 | +# %% |
| 89 | +# :math:`W` is provided in the TMY3 file but in other cases can be calculated |
| 90 | +# from temperature and relative humidity |
| 91 | +# (see :py:func:`pvlib.atmosphere.gueymard94_pw`). |
| 92 | + |
| 93 | +w = meteo.precipitable_water |
| 94 | + |
| 95 | +# %% |
| 96 | +# Calculation of Spectral Mismatch |
| 97 | +# -------------------------------- |
| 98 | +# Let's calculate the spectral mismatch factor using the three spectral factor |
| 99 | +# functions. First, we need to import some model coefficients for the SAPM |
| 100 | +# spectral factor function, which, unlike the other two functions, lacks |
| 101 | +# built-in coefficients. |
| 102 | + |
| 103 | +# Import some for a mc-Si module from the SAPM module database. |
| 104 | +module = pvlib.pvsystem.retrieve_sam('SandiaMod')['LG_LG290N1C_G3__2013_'] |
| 105 | +# |
| 106 | +# Calculate M using the three models for an mc-Si PV module. |
| 107 | +m_sapm = pvlib.spectrum.spectral_factor_sapm(airmass_absolute, module) |
| 108 | +m_pvspec = pvlib.spectrum.spectral_factor_pvspec(airmass_absolute, kc, |
| 109 | + 'multisi') |
| 110 | +m_fs = pvlib.spectrum.spectral_factor_firstsolar(w, airmass_absolute, |
| 111 | + 'multisi') |
| 112 | + |
| 113 | +df_results = pd.concat([m_sapm, m_pvspec, m_fs], axis=1) |
| 114 | +df_results.columns = ['SAPM', 'PVSPEC', 'FS'] |
| 115 | +# %% |
| 116 | +# Comparison Plots |
| 117 | +# ---------------- |
| 118 | +# We can plot the results to visualise variability between the models. Note |
| 119 | +# that this is not an exact comparison since the exact same PV modules has |
| 120 | +# not been modelled in each case, only the same module technology. |
| 121 | + |
| 122 | +# Plot M |
| 123 | +fig1, (ax1, ax2) = plt.subplots(2, 1) |
| 124 | +df_results.plot(ax=ax1, legend=False) |
| 125 | +ax1.set_xlabel('Day') |
| 126 | +ax1.set_ylabel('Spectral mismatch (-)') |
| 127 | +ax1.set_ylim(0.85, 1.15) |
| 128 | +ax1.legend(loc='upper center', frameon=False, ncols=3, |
| 129 | + bbox_to_anchor=(0.5, 1.3)) |
| 130 | + |
| 131 | +# We can also zoom in one one day, for example August 2nd. |
| 132 | +df_results.loc['2001-08-02'].plot(ax=ax2, legend=False) |
| 133 | +ax2.set_xlabel('Time') |
| 134 | +ax2.set_ylabel('Spectral mismatch (-)') |
| 135 | +ax2.set_ylim(0.85, 1.15) |
| 136 | + |
| 137 | +plt.tight_layout() |
| 138 | + |
| 139 | +plt.show() |
| 140 | + |
| 141 | +# %% |
| 142 | +# References |
| 143 | +# ---------- |
| 144 | +# .. [1] Daxini, R., and Wu, Y., 2023. "Review of methods to account |
| 145 | +# for the solar spectral influence on photovoltaic device performance." |
| 146 | +# Energy 286 |
| 147 | +# :doi:`10.1016/j.energy.2023.129461` |
| 148 | +# .. [2] Kasten, F. and Young, A.T., 1989. Revised optical air mass tables |
| 149 | +# and approximation formula. Applied Optics, 28(22), pp.4735-4738. |
| 150 | +# :doi:`10.1364/AO.28.004735` |
0 commit comments