"""
The ``irradiance`` module in the ``spectrum`` package provides functions for
calculations related to spectral irradiance data.
"""
import pvlib
from pvlib._deprecation import deprecated
import numpy as np
import pandas as pd
from pathlib import Path
from functools import partial
from scipy import constants
from scipy.integrate import trapezoid
[docs]
@deprecated(
since="0.11",
removal="0.12",
name="pvlib.spectrum.get_am15g",
alternative="pvlib.spectrum.get_reference_spectra",
addendum=(
"The new function reads more data. Use it with "
+ "standard='ASTM G173-03' and extract the 'global' column."
),
)
def get_am15g(wavelength=None):
r"""
Read the ASTM G173-03 AM1.5 global spectrum on a 37-degree tilted surface,
optionally interpolated to the specified wavelength(s).
Global (tilted) irradiance includes direct and diffuse irradiance from sky
and ground reflections, and is more formally called hemispherical
irradiance (on a tilted surface). In the context of photovoltaic systems
the irradiance on a flat receiver is frequently called plane-of-array (POA)
irradiance.
Parameters
----------
wavelength: 1-D sequence of numeric, optional
Wavelengths at which the spectrum is interpolated.
By default the 2002 wavelengths of the standard are returned. [nm].
Returns
-------
am15g: pandas.Series
The AM1.5g standard spectrum indexed by ``wavelength``. [W/(m²nm)].
Notes
-----
If ``wavelength`` is specified this function uses linear interpolation.
If the values in ``wavelength`` are too widely spaced, the integral of the
spectrum may deviate from the standard value of 1000.37 W/m².
The values in the data file provided with pvlib-python are copied from an
Excel file distributed by NREL, which is found here:
https://www.nrel.gov/grid/solar-resource/assets/data/astmg173.xls
More information about reference spectra is found here:
https://www.nrel.gov/grid/solar-resource/spectra-am1.5.html
See Also
--------
pvlib.spectrum.get_reference_spectra : reads also the direct and
extraterrestrial components of the spectrum.
References
----------
.. [1] ASTM "G173-03 Standard Tables for Reference Solar Spectral
Irradiances: Direct Normal and Hemispherical on 37° Tilted Surface."
""" # noqa: E501
# Contributed by Anton Driesse (@adriesse), PV Performance Labs. Aug. 2022
# modified by @echedey-ls, as a wrapper of spectrum.get_reference_spectra
standard = get_reference_spectra(wavelength, standard="ASTM G173-03")
return standard["global"]
[docs]
def get_reference_spectra(wavelengths=None, standard="ASTM G173-03"):
r"""
Read a standard spectrum specified by ``standard``, optionally
interpolated to the specified wavelength(s).
Defaults to ``ASTM G173-03`` AM1.5 standard [1]_, which returns
``extraterrestrial``, ``global`` and ``direct`` spectrum on a 37-degree
tilted surface, optionally interpolated to the specified wavelength(s).
Parameters
----------
wavelengths : numeric, optional
Wavelengths at which the spectrum is interpolated. [nm].
If not provided, the original wavelengths from the specified standard
are used. Values outside that range are filled with zeros.
standard : str, default "ASTM G173-03"
The reference standard to be read. Only the reference
``"ASTM G173-03"`` is available at the moment.
Returns
-------
standard_spectra : pandas.DataFrame
The standard spectrum by ``wavelength [nm]``. [W/(m²nm)].
Column names are ``extraterrestrial``, ``direct`` and ``global``.
Notes
-----
If ``wavelength`` is specified, linear interpolation is used.
If the values in ``wavelength`` are too widely spaced, the integral of each
spectrum may deviate from its standard value.
For global spectra, it is about 1000.37 W/m².
The values of the ASTM G173-03 provided with pvlib-python are copied from
an Excel file distributed by NREL, which is found here [2]_:
https://www.nrel.gov/grid/solar-resource/assets/data/astmg173.xls
Examples
--------
>>> from pvlib import spectrum
>>> am15 = spectrum.get_reference_spectra()
>>> am15_extraterrestrial, am15_global, am15_direct = \
>>> am15['extraterrestrial'], am15['global'], am15['direct']
>>> print(am15.head())
extraterrestrial global direct
wavelength
280.0 0.082 4.730900e-23 2.536100e-26
280.5 0.099 1.230700e-21 1.091700e-24
281.0 0.150 5.689500e-21 6.125300e-24
281.5 0.212 1.566200e-19 2.747900e-22
282.0 0.267 1.194600e-18 2.834600e-21
>>> am15 = spectrum.get_reference_spectra([300, 500, 800, 1100])
>>> print(am15)
extraterrestrial global direct
wavelength
300 0.45794 0.00102 0.000456
500 1.91600 1.54510 1.339100
800 1.12480 1.07250 0.988590
1100 0.60000 0.48577 0.461130
References
----------
.. [1] ASTM "G173-03 Standard Tables for Reference Solar Spectral
Irradiances: Direct Normal and Hemispherical on 37° Tilted Surface."
.. [2] “Reference Air Mass 1.5 Spectra,” www.nrel.gov.
https://www.nrel.gov/grid/solar-resource/spectra-am1.5.html
""" # Contributed by Echedey Luis, inspired by Anton Driesse (get_am15g)
SPECTRA_FILES = {
"ASTM G173-03": "ASTMG173.csv",
}
pvlib_datapath = Path(pvlib.__path__[0]) / "data"
try:
filepath = pvlib_datapath / SPECTRA_FILES[standard]
except KeyError:
raise ValueError(
f"Invalid standard identifier '{standard}'. Available "
+ "identifiers are: "
+ ", ".join(SPECTRA_FILES.keys())
)
standard = pd.read_csv(
filepath,
header=1, # expect first line of description, then column names
index_col=0, # first column is "wavelength"
dtype=float,
)
if wavelengths is not None:
interpolator = partial(
np.interp, xp=standard.index, left=0.0, right=0.0
)
standard = pd.DataFrame(
index=wavelengths,
data={
col: interpolator(x=wavelengths, fp=standard[col])
for col in standard.columns
},
)
return standard
[docs]
def average_photon_energy(spectra):
r"""
Calculate the average photon energy of one or more spectral irradiance
distributions.
Parameters
----------
spectra : pandas.Series or pandas.DataFrame
Spectral irradiance, must be positive [Wm⁻²nm⁻¹].
See :term:`spectra`.
A single spectrum must be a :py:class:`pandas.Series` with wavelength
[nm] as the index, while multiple spectra must be rows in a
:py:class:`pandas.DataFrame` with column headers as wavelength [nm].
Returns
-------
ape : numeric or pandas.Series
Average Photon Energy [eV].
Note: returns ``np.nan`` in the case of all-zero spectral irradiance
input.
Notes
-----
The average photon energy (APE) is an index used to characterise the solar
spectrum. It has been used widely in the physics literature since the
1900s, but its application for solar spectral irradiance characterisation
in the context of PV performance modelling was proposed in 2002 [1]_. The
APE is calculated based on the principle that a photon's energy is
inversely proportional to its wavelength:
.. math::
E_\gamma = \frac{hc}{\lambda},
where :math:`E_\gamma` is the energy of a photon with wavelength
:math:`\lambda`, :math:`h` is the Planck constant, and :math:`c` is the
speed of light. Therefore, the average energy of all photons within a
single spectral irradiance distribution provides an indication of the
general shape of the spectrum. A higher average photon energy
(shorter wavelength) indicates a blue-shifted spectrum, while a lower
average photon energy (longer wavelength) would indicate a red-shifted
spectrum. This value of the average photon energy can be calculated by
dividing the total energy in the spectrum by the total number of photons
in the spectrum as follows [1]_:
.. math::
\overline{E_\gamma} = \frac{1}{q} \cdot \frac{\int G(\lambda) \,
d\lambda}
{\int \Phi(\lambda) \, d\lambda}.
:math:`\Phi(\lambda)` is the photon flux density as a function of
wavelength, :math:`G(\lambda)` is the spectral irradiance, :math:`q` is the
elementary charge used here so that the average photon energy,
:math:`\overline{E_\gamma}`, is expressed in electronvolts (eV). The
integrals are computed over the full wavelength range of the ``spectra``
parameter.
References
----------
.. [1] Jardine, C., et al., 2002, January. Influence of spectral effects on
the performance of multijunction amorphous silicon cells. In Proc.
Photovoltaic in Europe Conference (pp. 1756-1759).
"""
if not isinstance(spectra, (pd.Series, pd.DataFrame)):
raise TypeError('`spectra` must be either a'
' pandas Series or DataFrame')
if (spectra < 0).any().any():
raise ValueError('Spectral irradiance data must be positive')
hclambda = pd.Series((constants.h*constants.c)/(spectra.T.index*1e-9))
hclambda.index = spectra.T.index
pfd = spectra.div(hclambda)
def integrate(e):
return trapezoid(e, x=e.T.index, axis=-1)
int_spectra = integrate(spectra)
int_pfd = integrate(pfd)
with np.errstate(invalid='ignore'):
ape = (1/constants.elementary_charge)*int_spectra/int_pfd
if isinstance(spectra, pd.DataFrame):
ape = pd.Series(ape, index=spectra.index)
return ape