Average Photon Energy Calculation#

Calculation of the Average Photon Energy from SPECTRL2 output.

Introduction#

This example demonstrates how to use the average_photon_energy() function to calculate the Average Photon Energy (APE, \(\overline{E_\gamma}\)) of spectral irradiance distributions. This example uses spectral irradiance simulated using spectrl2(), but the same method is applicable to spectral irradiance from any source. More information on the SPECTRL2 model can be found in [1]. The APE parameter is a useful indicator of the overall shape of the solar spectrum [2]. Higher (lower) APE values indicate a blue (red) shift in the spectrum and is one of a variety of such characterisation methods that is used in the PV performance literature [3].

To demonstrate this functionality, first we will simulate some spectra using spectrl2(). In this example, we will simulate spectra following a similar method to that which is followed in the Modeling Spectral Irradiance example, which reproduces a figure from [4]. The first step is to import the required packages and define some basic system parameters and meteorological conditions.

import pandas as pd
import matplotlib.pyplot as plt
from scipy.integrate import trapezoid
from pvlib import spectrum, solarposition, irradiance, atmosphere

lat, lon = 39.742, -105.18  # NREL SRRL location
tilt = 25
azimuth = 180  # south-facing system
pressure = 81190  # at 1828 metres AMSL, roughly
water_vapor_content = 0.5  # cm
tau500 = 0.1
ozone = 0.31  # atm-cm
albedo = 0.2

times = pd.date_range('2023-01-01 08:00', freq='h', periods=9,
                      tz='America/Denver')
solpos = solarposition.get_solarposition(times, lat, lon)
aoi = irradiance.aoi(tilt, azimuth, solpos.apparent_zenith, solpos.azimuth)

relative_airmass = atmosphere.get_relative_airmass(solpos.apparent_zenith,
                                                   model='kastenyoung1989')

Spectral simulation#

With all the necessary inputs now defined, we can model spectral irradiance using pvlib.spectrum.spectrl2(). As we are calculating spectra for more than one set of conditions, the function will return a dictionary containing 2-D arrays for the spectral irradiance components and a 1-D array of shape (122,) for wavelength. For each of the 2-D arrays, one dimension is for wavelength in nm and one is for irradiance in Wm⁻²nm⁻¹.

spectra_components = spectrum.spectrl2(
    apparent_zenith=solpos.apparent_zenith,
    aoi=aoi,
    surface_tilt=tilt,
    ground_albedo=albedo,
    surface_pressure=pressure,
    relative_airmass=relative_airmass,
    precipitable_water=water_vapor_content,
    ozone=ozone,
    aerosol_turbidity_500nm=tau500,
)

Visualising the spectral data#

Let’s take a look at the spectral irradiance data simulated hourly for eight hours on the first day of 2023.

plt.figure()
plt.plot(spectra_components['wavelength'], spectra_components['poa_global'])
plt.xlim(200, 2700)
plt.ylim(0, 1.8)
plt.ylabel(r"Spectral irradiance (Wm⁻²nm⁻¹)")
plt.xlabel(r"Wavelength (nm)")
time_labels = times.strftime("%H%M")
labels = [
    f"{t}, {am_:0.02f}"
    for t, am_ in zip(time_labels, relative_airmass)
]
plt.legend(labels, title="Time, AM")
plt.show()
average photon energy

Given the changing broadband irradiance throughout the day, it is not obvious from inspection how the relative distribution of light changes as a function of wavelength. We can normalise the spectral irradiance curves to visualise this shift in the shape of the spectrum over the course of the day. In this example, we normalise by dividing each spectral irradiance value by the total broadband irradiance, which we calculate by integrating the entire spectral irradiance distribution with respect to wavelength.

spectral_poa = spectra_components['poa_global']
wavelength = spectra_components['wavelength']

broadband_irradiance = trapezoid(spectral_poa, wavelength, axis=0)

spectral_poa_normalised = spectral_poa / broadband_irradiance

# Plot the normalised spectra
plt.figure()
plt.plot(wavelength, spectral_poa_normalised)
plt.xlim(200, 2700)
plt.ylim(0, 0.0018)
plt.ylabel(r"Normalised Irradiance (nm⁻¹)")
plt.xlabel(r"Wavelength (nm)")
time_labels = times.strftime("%H%M")
labels = [
    f"{t}, {am_:0.02f}"
    for t, am_ in zip(time_labels, relative_airmass)
]
plt.legend(labels, title="Time, AM")
plt.show()
average photon energy

We can now see from the normalised irradiance curves that at the start and end of the day, the spectrum is red shifted, meaning there is a greater proportion of longer wavelength radiation. Meanwhile, during the middle of the day, there is a greater prevalence of shorter wavelength radiation — a blue shifted spectrum.

How can we quantify this shift? This is where the average photon energy comes into play.

Calculating the average photon energy#

To calculate the APE, first we must convert our output spectra from the simulation into a compatible input for pvlib.spectrum.average_photon_energy(). Since we have more than one spectral irradiance distribution, a pandas.DataFrame is appropriate. We also need to set the column headers as wavelength, so each row is a single spectral irradiance distribution. It is important to remember here that the resulting APE values depend on the integration limits, i.e. the wavelength range of the spectral irradiance input. APE values are only comparable if calculated between the same integration limits. In this case, our APE values are calculated between 300nm and 4000nm.

spectra = pd.DataFrame(spectral_poa).T  # convert to dataframe and transpose
spectra.index = time_labels  # add time index
spectra.columns = wavelength  # add wavelength column headers

ape = spectrum.average_photon_energy(spectra)

We can update the normalised spectral irradiance plot to include the APE value of each spectral irradiance distribution in the legend. Note that the units of the APE are electronvolts (eV).

plt.figure()
plt.plot(wavelength, spectral_poa_normalised)
plt.xlim(200, 2700)
plt.ylim(0, 0.0018)
plt.ylabel(r"Normalised Irradiance (nm⁻¹)")
plt.xlabel(r"Wavelength (nm)")
time_labels = times.strftime("%H%M")
labels = [
    f"{t}, {ape_:0.02f}"
    for t, ape_ in zip(time_labels, ape)
]
plt.legend(labels,  title="Time, APE (eV)")
plt.show()
average photon energy

As expected, the morning and evening spectra have a lower APE while a higher APE is observed closer to the middle of the day. For reference, AM1.5 between 300 and 4000 nm is 1.4501 eV. This indicates that the simulated spectra are slightly red shifted with respect to the AM1.5 standard reference spectrum.

References#

Total running time of the script: (0 minutes 0.299 seconds)

Gallery generated by Sphinx-Gallery