Skip to content

Create function to calculate average photon energy #2140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4cf9961
create
RDaxini Jul 20, 2024
a9d8dd9
Update spectrum.rst
RDaxini Jul 20, 2024
513d708
Update test_spectrum.py
RDaxini Jul 20, 2024
f376d59
Update mismatch.py
RDaxini Aug 1, 2024
5eb7c13
Update mismatch.py
RDaxini Aug 1, 2024
3420a92
Update mismatch.py
RDaxini Aug 1, 2024
c2049ee
Update mismatch.py
RDaxini Aug 2, 2024
afb06f4
docs, tests
RDaxini Aug 2, 2024
928c38c
docs, data checks, tests
RDaxini Aug 2, 2024
d63298a
Update mismatch.py
RDaxini Aug 2, 2024
3e46831
Update mismatch.py
RDaxini Aug 4, 2024
2f4b17e
Apply suggestions from code review
RDaxini Aug 5, 2024
5a66414
remove comments, inverse fraction
RDaxini Aug 5, 2024
ec5bc6c
remove comment
RDaxini Aug 5, 2024
2534ddd
Merge remote-tracking branch 'upstream/main' into calc_ape
RDaxini Aug 5, 2024
6cb9689
Update irradiance.py
RDaxini Aug 5, 2024
c9b4107
Merge remote-tracking branch 'upstream/main' into calc_ape
RDaxini Aug 8, 2024
1720d7d
Update test_irradiance.py
RDaxini Aug 8, 2024
a869f83
Update test_irradiance.py
RDaxini Aug 8, 2024
af63875
Update irradiance.py
RDaxini Aug 8, 2024
43ae7d3
mixed up my lambdas and gammas:(
RDaxini Aug 8, 2024
c2c373d
change variable name spectral_irr -> spectrum
RDaxini Aug 8, 2024
698d9d7
update variable name in docstring + error messages
RDaxini Aug 9, 2024
e9919f5
Update irradiance.py
RDaxini Aug 9, 2024
458bf16
Update test_irradiance.py
RDaxini Aug 12, 2024
ecba3eb
Apply suggestions from code review
RDaxini Aug 12, 2024
0b95307
Update irradiance.py
RDaxini Aug 12, 2024
39fe12c
Update v0.11.1.rst
RDaxini Aug 13, 2024
112747e
Update irradiance.py
RDaxini Aug 14, 2024
ddc70c5
Update irradiance.py
RDaxini Aug 14, 2024
a8e72d8
Update test_irradiance.py
RDaxini Aug 14, 2024
80bf690
Update test_mismatch.py
RDaxini Aug 14, 2024
1bafc90
Update v0.11.1.rst
RDaxini Aug 14, 2024
6a21228
Update irradiance.py
RDaxini Aug 14, 2024
c224e05
update output datatype and associated tests
RDaxini Aug 14, 2024
ace5a65
Update test_irradiance.py
RDaxini Aug 14, 2024
072eeeb
Update docs/sphinx/source/whatsnew/v0.11.1.rst
RDaxini Aug 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ Spectrum
spectrum.spectral_factor_jrc
spectrum.sr_to_qe
spectrum.qe_to_sr
spectrum.average_photon_energy
3 changes: 2 additions & 1 deletion pvlib/spectrum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
spectral_factor_pvspec,
spectral_factor_jrc,
sr_to_qe,
qe_to_sr
qe_to_sr,
average_photon_energy
)
93 changes: 93 additions & 0 deletions pvlib/spectrum/mismatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import scipy.constants
from scipy.integrate import trapezoid
from scipy.interpolate import interp1d
from scipy import constants

from pathlib import Path
from warnings import warn
Expand Down Expand Up @@ -1100,3 +1101,95 @@ def qe_to_sr(qe, wavelength=None, normalize=False):
spectral_responsivity = normalize_max2one(spectral_responsivity)

return spectral_responsivity


def average_photon_energy(spectral_irr):
r"""
Calculate the average photon energy of one or more spectral irradiance
distributions.

Parameters
----------
spectral_irr : pandas.Series or pandas.DataFrame

Spectral irradiance, must be positive. [Wm⁻²nm⁻¹]

A single spectrum must be a :py:class:`pandas.Series` with wavelength
[nm] as the index, while multiple spectra must be a
:py:class:`pandas.DataFrame` with column headers as wavelength [nm].

Returns
-------
ape : numeric or array
Average Photon Energy [eV].

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 [1]_. The APE
is calculated based on the principle that a photon's wavelength is
inversely proportional to its energy:

.. 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 number of photons in the spectrum by the total energy in
the spectrum as follows [1]_:

.. math::

\varphi = \frac{1}{q} \cdot \frac{\int_a^b \Phi_\lambda \, d\lambda}
{\int_a^b E_\lambda \, d\lambda}.

:math:`\Phi_\lambda` is the photon flux density as a function of
wavelength, :math:`q` is the elementary charge used here so that the
average photon energy, :math:`\varphi`, is expressed in electronvolts (eV).
The integration limits, :math:`a` and :math:`b`, define the wavelength
range within which the APE is calculated. By default, this function
calculates the value for APE based on full wavelength range of the
``spectral_irr`` 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).

"""

si = spectral_irr

if not isinstance(si, (pd.Series, pd.DataFrame)):
raise TypeError('`spectral_irr` must be either a'
' pandas Series or DataFrame')
# check si type

if (si < 0).any().any():
raise ValueError('Spectral irradiance data must be positive')
# check if si contains any negative irradiance values

hclambda = pd.Series((constants.h*constants.c)/(si.T.index*1e-9))
hclambda.index = si.T.index # set wavelength as the index
pfd = si.div(hclambda) # calculate the photon flux density

def integrate(e): # define a helper function
return trapezoid(e, x=e.T.index, axis=-1)

int_si = integrate(si) # integrate spectral irradiance wrt wavelength
int_pfd = integrate(pfd) # integrate photon flux density wrt wavelength

ape = (1/constants.elementary_charge)*int_si/int_pfd
# calculate the average photon energy (ape) in electronvolts (eV)

return ape
57 changes: 57 additions & 0 deletions pvlib/tests/test_spectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,3 +576,60 @@ def test_qe_and_sr_reciprocal_conversion(sr_and_eqe_fixture):
assert_allclose(sr, sr_and_eqe_fixture["spectral_response"])
qe = spectrum.sr_to_qe(sr)
assert_allclose(qe, sr_and_eqe_fixture["quantum_efficiency"])


def test_average_photon_energy_series():
# test that the APE is calculated correctly with single spectrum
# series input

si = spectrum.get_reference_spectra()
si = si['global']

ape = spectrum.average_photon_energy(si)

expected = 1.45017

assert_allclose(ape, expected, rtol=1e-4)


def test_average_photon_energy_dataframe():
# test that the APE is calculated correctly with multiple spectra
# dataframe input

si = spectrum.get_reference_spectra().T
# Generate three spectra and orientate the dataframe according to the
# function requirements

ape = spectrum.average_photon_energy(si)

expected = [1.36848, 1.45017, 1.40885]

assert_allclose(ape, expected, rtol=1e-4)


def test_average_photon_energy_invalid_type():
# test that spectral_irr argument is either a pandas Series or dataframe
spectral_irr = 5
with pytest.raises(TypeError, match='must be either a pandas Series or'
' DataFrame'):
spectrum.average_photon_energy(spectral_irr)


def test_average_photon_energy_neg_irr_series():
# test for handling of negative spectral irradiance values with a
# pandas Series input

spectral_irr = spectrum.get_reference_spectra()['global']*-1

with pytest.raises(ValueError, match='must be positive'):
spectrum.average_photon_energy(spectral_irr)


def test_average_photon_energy_neg_irr_dataframe():
# test for handling of negative spectral irradiance values with a
# pandas Series input

spectral_irr = spectrum.get_reference_spectra().T*-1

with pytest.raises(ValueError, match='must be positive'):
spectrum.average_photon_energy(spectral_irr)
Loading