|
| 1 | +""" |
| 2 | +Average Photon Energy Calculation |
| 3 | +================================= |
| 4 | +
|
| 5 | +Calculation of the Average Photon Energy from SPECTRL2 output. |
| 6 | +""" |
| 7 | + |
| 8 | +# %% |
| 9 | +# Introduction |
| 10 | +# ------------ |
| 11 | +# This example demonstrates how to use the |
| 12 | +# :py:func:`~pvlib.spectrum.average_photon_energy` function to calculate the |
| 13 | +# Average Photon Energy (APE, :math:`\overline{E_\gamma}`) of spectral |
| 14 | +# irradiance distributions. This example uses spectral irradiance simulated |
| 15 | +# using :py:func:`~pvlib.spectrum.spectrl2`, but the same method is |
| 16 | +# applicable to spectral irradiance from any source. |
| 17 | +# More information on the SPECTRL2 model can be found in [1]_. |
| 18 | +# The APE parameter is a useful indicator of the overall shape of the solar |
| 19 | +# spectrum [2]_. Higher (lower) APE values indicate a blue (red) shift in the |
| 20 | +# spectrum and is one of a variety of such characterisation methods that is |
| 21 | +# used in the PV performance literature [3]_. |
| 22 | +# |
| 23 | +# To demonstrate this functionality, first we will simulate some spectra |
| 24 | +# using :py:func:`~pvlib.spectrum.spectrl2`. In this example, we will simulate |
| 25 | +# spectra following a similar method to that which is followed in the |
| 26 | +# :ref:`sphx_glr_gallery_spectrum_plot_spectrl2_fig51A.py` example, which |
| 27 | +# reproduces a figure from [4]_. The first step is to import the required |
| 28 | +# packages and define some basic system parameters and meteorological |
| 29 | +# conditions. |
| 30 | + |
| 31 | +# %% |
| 32 | +import pandas as pd |
| 33 | +import matplotlib.pyplot as plt |
| 34 | +from scipy.integrate import trapezoid |
| 35 | +from pvlib import spectrum, solarposition, irradiance, atmosphere |
| 36 | + |
| 37 | +lat, lon = 39.742, -105.18 # NREL SRRL location |
| 38 | +tilt = 25 |
| 39 | +azimuth = 180 # south-facing system |
| 40 | +pressure = 81190 # at 1828 metres AMSL, roughly |
| 41 | +water_vapor_content = 0.5 # cm |
| 42 | +tau500 = 0.1 |
| 43 | +ozone = 0.31 # atm-cm |
| 44 | +albedo = 0.2 |
| 45 | + |
| 46 | +times = pd.date_range('2023-01-01 08:00', freq='h', periods=9, |
| 47 | + tz='America/Denver') |
| 48 | +solpos = solarposition.get_solarposition(times, lat, lon) |
| 49 | +aoi = irradiance.aoi(tilt, azimuth, solpos.apparent_zenith, solpos.azimuth) |
| 50 | + |
| 51 | +relative_airmass = atmosphere.get_relative_airmass(solpos.apparent_zenith, |
| 52 | + model='kastenyoung1989') |
| 53 | + |
| 54 | +# %% |
| 55 | +# Spectral simulation |
| 56 | +# ------------------------- |
| 57 | +# With all the necessary inputs now defined, we can model spectral irradiance |
| 58 | +# using :py:func:`pvlib.spectrum.spectrl2`. As we are calculating spectra for |
| 59 | +# more than one set of conditions, the function will return a dictionary |
| 60 | +# containing 2-D arrays for the spectral irradiance components and a 1-D array |
| 61 | +# of shape (122,) for wavelength. For each of the 2-D arrays, one dimension is |
| 62 | +# for wavelength in nm and one is for irradiance in Wm⁻²nm⁻¹. |
| 63 | + |
| 64 | +spectra_components = spectrum.spectrl2( |
| 65 | + apparent_zenith=solpos.apparent_zenith, |
| 66 | + aoi=aoi, |
| 67 | + surface_tilt=tilt, |
| 68 | + ground_albedo=albedo, |
| 69 | + surface_pressure=pressure, |
| 70 | + relative_airmass=relative_airmass, |
| 71 | + precipitable_water=water_vapor_content, |
| 72 | + ozone=ozone, |
| 73 | + aerosol_turbidity_500nm=tau500, |
| 74 | +) |
| 75 | + |
| 76 | +# %% |
| 77 | +# Visualising the spectral data |
| 78 | +# ----------------------------- |
| 79 | +# Let's take a look at the spectral irradiance data simulated hourly for eight |
| 80 | +# hours on the first day of 2023. |
| 81 | + |
| 82 | +plt.figure() |
| 83 | +plt.plot(spectra_components['wavelength'], spectra_components['poa_global']) |
| 84 | +plt.xlim(200, 2700) |
| 85 | +plt.ylim(0, 1.8) |
| 86 | +plt.ylabel(r"Spectral irradiance (Wm⁻²nm⁻¹)") |
| 87 | +plt.xlabel(r"Wavelength (nm)") |
| 88 | +time_labels = times.strftime("%H%M") |
| 89 | +labels = [ |
| 90 | + f"{t}, {am_:0.02f}" |
| 91 | + for t, am_ in zip(time_labels, relative_airmass) |
| 92 | +] |
| 93 | +plt.legend(labels, title="Time, AM") |
| 94 | +plt.show() |
| 95 | + |
| 96 | +# %% |
| 97 | +# Given the changing broadband irradiance throughout the day, it is not obvious |
| 98 | +# from inspection how the relative distribution of light changes as a function |
| 99 | +# of wavelength. We can normalise the spectral irradiance curves to visualise |
| 100 | +# this shift in the shape of the spectrum over the course of the day. In |
| 101 | +# this example, we normalise by dividing each spectral irradiance value by the |
| 102 | +# total broadband irradiance, which we calculate by integrating the entire |
| 103 | +# spectral irradiance distribution with respect to wavelength. |
| 104 | + |
| 105 | +spectral_poa = spectra_components['poa_global'] |
| 106 | +wavelength = spectra_components['wavelength'] |
| 107 | + |
| 108 | +broadband_irradiance = trapezoid(spectral_poa, wavelength, axis=0) |
| 109 | + |
| 110 | +spectral_poa_normalised = spectral_poa / broadband_irradiance |
| 111 | + |
| 112 | +# Plot the normalised spectra |
| 113 | +plt.figure() |
| 114 | +plt.plot(wavelength, spectral_poa_normalised) |
| 115 | +plt.xlim(200, 2700) |
| 116 | +plt.ylim(0, 0.0018) |
| 117 | +plt.ylabel(r"Normalised Irradiance (nm⁻¹)") |
| 118 | +plt.xlabel(r"Wavelength (nm)") |
| 119 | +time_labels = times.strftime("%H%M") |
| 120 | +labels = [ |
| 121 | + f"{t}, {am_:0.02f}" |
| 122 | + for t, am_ in zip(time_labels, relative_airmass) |
| 123 | +] |
| 124 | +plt.legend(labels, title="Time, AM") |
| 125 | +plt.show() |
| 126 | + |
| 127 | +# %% |
| 128 | +# We can now see from the normalised irradiance curves that at the start and |
| 129 | +# end of the day, the spectrum is red shifted, meaning there is a greater |
| 130 | +# proportion of longer wavelength radiation. Meanwhile, during the middle of |
| 131 | +# the day, there is a greater prevalence of shorter wavelength radiation — a |
| 132 | +# blue shifted spectrum. |
| 133 | +# |
| 134 | +# How can we quantify this shift? This is where the average photon energy comes |
| 135 | +# into play. |
| 136 | + |
| 137 | +# %% |
| 138 | +# Calculating the average photon energy |
| 139 | +# ------------------------------------- |
| 140 | +# To calculate the APE, first we must convert our output spectra from the |
| 141 | +# simulation into a compatible input for |
| 142 | +# :py:func:`pvlib.spectrum.average_photon_energy`. Since we have more than one |
| 143 | +# spectral irradiance distribution, a :py:class:`pandas.DataFrame` is |
| 144 | +# appropriate. We also need to set the column headers as wavelength, so each |
| 145 | +# row is a single spectral irradiance distribution. It is important to remember |
| 146 | +# here that the resulting APE values depend on the integration limits, i.e. |
| 147 | +# the wavelength range of the spectral irradiance input. APE values are only |
| 148 | +# comparable if calculated between the same integration limits. In this case, |
| 149 | +# our APE values are calculated between 300nm and 4000nm. |
| 150 | + |
| 151 | +spectra = pd.DataFrame(spectral_poa).T # convert to dataframe and transpose |
| 152 | +spectra.index = time_labels # add time index |
| 153 | +spectra.columns = wavelength # add wavelength column headers |
| 154 | + |
| 155 | +ape = spectrum.average_photon_energy(spectra) |
| 156 | + |
| 157 | +# %% |
| 158 | +# We can update the normalised spectral irradiance plot to include the APE |
| 159 | +# value of each spectral irradiance distribution in the legend. Note that the |
| 160 | +# units of the APE are electronvolts (eV). |
| 161 | + |
| 162 | +plt.figure() |
| 163 | +plt.plot(wavelength, spectral_poa_normalised) |
| 164 | +plt.xlim(200, 2700) |
| 165 | +plt.ylim(0, 0.0018) |
| 166 | +plt.ylabel(r"Normalised Irradiance (nm⁻¹)") |
| 167 | +plt.xlabel(r"Wavelength (nm)") |
| 168 | +time_labels = times.strftime("%H%M") |
| 169 | +labels = [ |
| 170 | + f"{t}, {ape_:0.02f}" |
| 171 | + for t, ape_ in zip(time_labels, ape) |
| 172 | +] |
| 173 | +plt.legend(labels, title="Time, APE (eV)") |
| 174 | +plt.show() |
| 175 | + |
| 176 | +# %% |
| 177 | +# As expected, the morning and evening spectra have a lower APE while a higher |
| 178 | +# APE is observed closer to the middle of the day. For reference, AM1.5 between |
| 179 | +# 300 and 4000 nm is 1.4501 eV. This indicates that the simulated spectra are |
| 180 | +# slightly red shifted with respect to the AM1.5 standard reference spectrum. |
| 181 | + |
| 182 | +# %% |
| 183 | +# References |
| 184 | +# ---------- |
| 185 | +# .. [1] Bird, R, and Riordan, C., 1984, "Simple solar spectral model for |
| 186 | +# direct and diffuse irradiance on horizontal and tilted planes at the |
| 187 | +# earth's surface for cloudless atmospheres", NREL Technical Report |
| 188 | +# TR-215-2436 :doi:`10.2172/5986936` |
| 189 | +# .. [2] Jardine, C., et al., 2002, January. Influence of spectral effects on |
| 190 | +# the performance of multijunction amorphous silicon cells. In Proc. |
| 191 | +# Photovoltaic in Europe Conference (pp. 1756-1759) |
| 192 | +# .. [3] Daxini, R., and Wu, Y., 2023. "Review of methods to account |
| 193 | +# for the solar spectral influence on photovoltaic device performance." |
| 194 | +# Energy 286 :doi:`10.1016/j.energy.2023.129461` |
| 195 | +# .. [4] Bird Simple Spectral Model: spectrl2_2.c |
| 196 | +# https://www.nrel.gov/grid/solar-resource/spectral.html |
| 197 | +# (Last accessed: 18/09/2024) |
0 commit comments