|
25 | 25 | # import the required packages and define some basic system parameters and |
26 | 26 | # and meteorological conditions. |
27 | 27 | # %% |
28 | | -from pvlib import spectrum, solarposition, irradiance, atmosphere |
| 28 | +import numpy as np |
29 | 29 | import pandas as pd |
30 | 30 | import matplotlib.pyplot as plt |
| 31 | +from scipy.integrate import trapezoid |
| 32 | +from pvlib import spectrum, solarposition, irradiance, atmosphere |
31 | 33 |
|
32 | 34 | lat, lon = 39.742, -105.18 # NREL SRRL location |
33 | 35 | tilt = 25 |
|
38 | 40 | ozone = 0.31 # atm-cm |
39 | 41 | albedo = 0.2 |
40 | 42 |
|
41 | | -times = pd.date_range('2023-01-01 12:00', freq='D', periods=7, |
| 43 | +times = pd.date_range('2023-01-01 08:00', freq='h', periods=9, |
42 | 44 | tz='America/Denver') |
43 | 45 | solpos = solarposition.get_solarposition(times, lat, lon) |
44 | 46 | aoi = irradiance.aoi(tilt, azimuth, solpos.apparent_zenith, solpos.azimuth) |
|
58 | 60 | # The next section will show how to convert this output into a suitable |
59 | 61 | # input for :pyfunc:`~average_photon_energy`. |
60 | 62 |
|
61 | | -spectra = spectrum.spectrl2( |
| 63 | +spectra_components = spectrum.spectrl2( |
62 | 64 | apparent_zenith=solpos.apparent_zenith, |
63 | 65 | aoi=aoi, |
64 | 66 | surface_tilt=tilt, |
|
69 | 71 | ozone=ozone, |
70 | 72 | aerosol_turbidity_500nm=tau500, |
71 | 73 | ) |
| 74 | + |
72 | 75 | # %% |
73 | | -# another section |
74 | | -# -------------------------------- |
| 76 | +# Visualising the spectral data |
| 77 | +# ----------------------------- |
| 78 | +# Let's take a look at the spectral irradiance data simulated on the hour for |
| 79 | +# eight hours on the first day of 2023. |
| 80 | + |
| 81 | +plt.figure() |
| 82 | +plt.plot(spectra_components['wavelength'], spectra_components['poa_global']) |
| 83 | +plt.xlim(200, 2700) |
| 84 | +plt.ylim(0, 1.8) |
| 85 | +plt.ylabel(r"Irradiance (Wm⁻²nm⁻¹") |
| 86 | +plt.xlabel(r"Wavelength (nm)") |
| 87 | +time_labels = times.strftime("%H:%M") |
| 88 | +labels = [ |
| 89 | + "{}, AM {:0.02f}".format(*vals) |
| 90 | + for vals in zip(time_labels, relative_airmass) |
| 91 | +] |
| 92 | +plt.legend(labels) |
| 93 | +plt.show() |
| 94 | + |
| 95 | +# Given the changing irradiance throughout the day, it is not obvious from |
| 96 | +# inspection how the relative distribution of light changes as a function of |
| 97 | +# wavelength. We can normalise the spectral irradiance curves to get an idea |
| 98 | +# of this shift in the shape of the spectrum over the course of the day. In |
| 99 | +# this example, we normalise by dividing each spectral irradiance value by the |
| 100 | +# total irradiance, as calculated by integrating the entire spectral irradiance |
| 101 | +# distribution with respect to wavelength. |
| 102 | + |
| 103 | +poa_global = spectra_components['poa_global'] |
| 104 | +wavelength = spectra_components['wavelength'] |
| 105 | + |
| 106 | +broadband_irradiance = np.array([trapezoid(poa_global[:, i], wavelength) |
| 107 | + for i in range(poa_global.shape[1])]) |
| 108 | + |
| 109 | +poa_global_normalised = poa_global / broadband_irradiance |
| 110 | + |
| 111 | +# Plot the normalised spectra |
| 112 | + |
| 113 | +plt.figure() |
| 114 | +plt.plot(wavelength, poa_global_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 | + "{}, AM {:0.02f}".format(*vals) |
| 122 | + for vals in zip(time_labels, relative_airmass) |
| 123 | +] |
| 124 | +plt.legend(labels) |
| 125 | +plt.show() |
| 126 | + |
| 127 | +# Now we can see from XX figure numbers? XX that at the start and end of the |
| 128 | +# day, the spectrum is red shifted, meaning there is a greater proportion of |
| 129 | +# longer wavelength radiation. Meanwhile, during the middle of the day there is |
| 130 | +# a blue shift in the spectral distribution, indicating a greater prevalence of |
| 131 | +# shorter wavelength radiation. |
| 132 | + |
| 133 | +# How can we quantify this shift? That is where the average photon energy comes |
| 134 | +# into play. |
| 135 | + |
75 | 136 | # %% |
76 | | -# |
| 137 | +# Calculating the average photon energy |
| 138 | +# ------------------------------------- |
| 139 | +# To calculate the APE, first we must convert our output spectra from from the |
| 140 | +# simulation into a compatible input for |
| 141 | +# :pyfunc:`pvlib.spectrum.average_photon_energy`. Since we have more than one |
| 142 | +# spectral irradiance distribution, a :py:class:`pandas.DataFrame` is |
| 143 | +# appropriate. We also need to set the column headers as wavelength, so each |
| 144 | +# row is a single spectral irradiance distribution. It is important to remember |
| 145 | +# here that the calculation of APE is dependent on the integration limits, i.e. |
| 146 | +# the wavelength range of the spectral irradiance input. APE values are only |
| 147 | +# comparable if calculated between the same integration limits. In this case, |
| 148 | +# our APE values are calculated between 300nm and 4000nm. |
| 149 | + |
| 150 | +spectra = pd.DataFrame(poa_global).T # convert to dataframe and transpose |
| 151 | +spectra.index = time_labels # add time index |
| 152 | +spectra.columns = wavelength # add wavelength column headers |
| 153 | + |
| 154 | +ape = spectrum.average_photon_energy(spectra) |
| 155 | + |
| 156 | +# XX table? add values / arrow(s) to graph XX |
77 | 157 |
|
78 | 158 | # %% |
79 | 159 | # References |
|
90 | 170 | # Energy 286 :doi:`10.1016/j.energy.2023.129461` |
91 | 171 | # .. [4] Bird Simple Spectral Model: spectrl2_2.c. |
92 | 172 | # https://www.nrel.gov/grid/solar-resource/spectral.html |
93 | | - |
|
0 commit comments