Skip to content
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

Quantum efficiency & spectral response conversion funcs #2041

Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
fdc65f0
Add quantum_efficiency_to_spectral_responsivity and spectral_responsi…
echedey-ls Mar 24, 2024
7c45240
Merge branch 'main' into quantum-efficiency-&-spectral-response-conve…
echedey-ls Mar 24, 2024
52a4752
Update v0.10.5.rst
echedey-ls Mar 24, 2024
8fcabde
Add docstring examples
echedey-ls Apr 11, 2024
69f5e0d
Merge branch 'main' into quantum-efficiency-&-spectral-response-conve…
echedey-ls May 3, 2024
7865c03
Merge branch 'main' into quantum-efficiency-&-spectral-response-conve…
echedey-ls May 6, 2024
1b5be03
Update v0.11.0.rst
echedey-ls May 6, 2024
3631830
Linter
echedey-ls May 6, 2024
75a27d6
More linter xD
echedey-ls May 6, 2024
b057d28
Remove reference links from first sentence
echedey-ls May 6, 2024
27f160e
remove old whatsme entries
echedey-ls May 10, 2024
d7d9b81
rename funcs and change versionadded's
echedey-ls May 10, 2024
0b5ad4b
Update v0.10.5.rst
echedey-ls May 10, 2024
28ebce8
This is test driven development
echedey-ls May 21, 2024
9bfa003
apply normalization to functions
echedey-ls May 21, 2024
7b254a4
Merge branch 'main' into quantum-efficiency-&-spectral-response-conve…
echedey-ls May 23, 2024
78cbc4c
Merge branch 'main' into quantum-efficiency-&-spectral-response-conve…
echedey-ls May 24, 2024
4d6b440
Update test_spectrum.py
echedey-ls May 24, 2024
0b00fb7
Update test_tools.py
echedey-ls May 24, 2024
8c30356
Units formatting
echedey-ls May 24, 2024
606e9b6
Links
echedey-ls May 24, 2024
83d875e
I'm obsessed with math mode
echedey-ls May 25, 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 @@ -13,3 +13,5 @@ Spectrum
spectrum.spectral_factor_caballero
spectrum.spectral_factor_firstsolar
spectrum.spectral_factor_sapm
spectrum.spectral_responsivity_to_quantum_efficiency
spectrum.quantum_efficiency_to_spectral_responsivity
17 changes: 17 additions & 0 deletions docs/sphinx/source/whatsnew/v0.10.5.rst
echedey-ls marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
.. _whatsnew_01050:


v0.10.5 (Anticipated June 2024)
-------------------------------


Deprecations
~~~~~~~~~~~~


Enhancements
~~~~~~~~~~~~
* Added :py:func:`pvlib.spectrum.spectral_responsivity_to_quantum_efficiency`
and :py:func:`pvlib.spectrum.quantum_efficiency_to_spectral_responsivity`
to convert quantum efficiencies into spectral responsivities and viceversa.
(:issue:`1963`, :pull:`TODO`)
v0.10.5 (May 6, 2024)
---------------------

Expand Down Expand Up @@ -33,6 +47,9 @@ Requirements

Contributors
~~~~~~~~~~~~
* Echedey Luis (:ghuser:`echedey-ls`)
* Mark Mikofski (:ghuser:`mikofski`)
* Mark Campanelli (:ghuser:`markcampanelli`)
* Cliff Hansen (:ghuser:`cwhanse`)
* :ghuser:`apct69`
* Mark Mikofski (:ghuser:`mikofski`)
Expand Down
5 changes: 5 additions & 0 deletions docs/sphinx/source/whatsnew/v0.11.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Deprecations

Enhancements
~~~~~~~~~~~~
* Added conversion functions from spectral response :math:`[A/W]` to quantum
efficiency :math:`[unitless]` and vice versa. The conversion functions are
:py:func:`spectral_responsivity_to_quantum_efficiency` and :py:func:`quantum_efficiency_to_spectral_responsivity` respectively.
(:issue:`2040`, :pull:`2041`)
echedey-ls marked this conversation as resolved.
Show resolved Hide resolved


Bug fixes
Expand All @@ -35,3 +39,4 @@ Requirements

Contributors
~~~~~~~~~~~~
* Mark Campanelli (:ghuser:`markcampanelli`)
2 changes: 2 additions & 0 deletions pvlib/spectrum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@
spectral_factor_caballero,
spectral_factor_firstsolar,
spectral_factor_sapm,
spectral_responsivity_to_quantum_efficiency,
quantum_efficiency_to_spectral_responsivity,
)
191 changes: 189 additions & 2 deletions pvlib/spectrum/mismatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@
import pvlib
import numpy as np
import pandas as pd
from scipy.interpolate import interp1d
import scipy.constants
from scipy.integrate import trapezoid
from scipy.interpolate import interp1d
import os

from warnings import warn


_PLANCK_BY_LIGHT_SPEED_OVER_ELEMENTAL_CHARGE_BY_BILLION = (
scipy.constants.speed_of_light
* scipy.constants.Planck
/ scipy.constants.elementary_charge
* 1e9
)


def get_example_spectral_response(wavelength=None):
'''
Generate a generic smooth spectral response (SR) for tests and experiments.
Expand Down Expand Up @@ -154,7 +163,7 @@ def calc_spectral_mismatch_field(sr, e_sun, e_ref=None):

e_sun: pandas.DataFrame or pandas.Series
One or more measured solar irradiance spectra in a pandas.DataFrame
having wavelength in nm as column index. A single spectrum may be
having wavelength in nm as column index. A single spectrum may be
be given as a pandas.Series having wavelength in nm as index.
[(W/m^2)/nm]

Expand Down Expand Up @@ -571,3 +580,181 @@ def spectral_factor_caballero(precipitable_water, airmass_absolute, aod500,
)
modifier = f_AM + f_AOD + f_PW # Eq 5
return modifier


def spectral_responsivity_to_quantum_efficiency(sr, wavelength=None):
echedey-ls marked this conversation as resolved.
Show resolved Hide resolved
"""
Convert spectral responsivities to quantum efficiencies.
If ``wavelength`` is not provided, the spectral responsivity ``sr`` must be
a :py:class:`pandas.Series` or :py:class:`pandas.DataFrame`, with the
wavelengths in the index.

Provide wavelengths in nanometers :math:`[nm]`.

Conversion is described in [1]_.

.. versionadded:: 0.10.5
echedey-ls marked this conversation as resolved.
Show resolved Hide resolved

Parameters
----------
sr : numeric, pandas.Series or pandas.DataFrame
Spectral response.
Index must be the wavelength in nanometers :math:`nm`.

wavelength : numeric, optional
Points where spectral response is measured, in nanometers :math:`nm`.

Returns
-------
quantum_efficiency : numeric, same type as ``sr``
Quantum efficiency in the interval :math:`[0, 1]`.

Notes
-----
- If ``sr`` is of type ``pandas.Series`` or ``pandas.DataFrame``,
column names will remain unchanged in the returned object.
- If ``wavelength`` is provided it will be used independently of the
datatype of ``sr``.

Examples
--------
>>> import numpy as np
>>> from pvlib import spectrum
>>> wavelengths = np.array([350, 550, 750])
>>> spectral_response = np.array([0.25, 0.40, 0.57])
>>> quantum_efficiency = \
>>> spectrum.spectral_responsivity_to_quantum_efficiency(
>>> spectral_response, wavelengths
>>> )
>>> quantum_efficiency
array([0.88560142, 0.90170326, 0.94227991])

>>> spectral_response_series = \
>>> pd.Series(spectral_response, index=wavelengths, name="dataset")
>>> qe = spectrum.spectral_responsivity_to_quantum_efficiency(
>>> spectral_response_series
>>> )
>>> qe
350 0.885601
550 0.901703
750 0.942280
Name: dataset, dtype: float64

References
----------
.. [1] “Spectral Response,” PV Performance Modeling Collaborative (PVPMC).
https://pvpmc.sandia.gov/modeling-guide/2-dc-module-iv/
effective-irradiance/spectral-response/
.. [2] “Spectral Response | PVEducation,” www.pveducation.org.
https://www.pveducation.org/pvcdrom/solar-cell-operation/
spectral-response
echedey-ls marked this conversation as resolved.
Show resolved Hide resolved

See Also
--------
pvlib.spectrum.quantum_efficiency_to_spectral_responsivity
"""
if wavelength is None:
if hasattr(sr, "index"): # true for pandas objects
# use reference to index values instead of index alone so
# sr / wavelength returns a series with the same name
wavelength = sr.index.array
else:
raise TypeError(
"'sr' must have an '.index' attribute"
+ " or 'wavelength' must be provided"
)
quantum_efficiency = (
sr
/ wavelength
* _PLANCK_BY_LIGHT_SPEED_OVER_ELEMENTAL_CHARGE_BY_BILLION
)
return quantum_efficiency


def quantum_efficiency_to_spectral_responsivity(qe, wavelength=None):
"""
Convert quantum efficiencies to spectral responsivities.
If ``wavelength`` is not provided, the quantum efficiency ``qe`` must be
a :py:class:`pandas.Series` or :py:class:`pandas.DataFrame`, with the
wavelengths in the index.

Provide wavelengths in nanometers :math:`[nm]`.

Conversion is described in [1]_.

.. versionadded:: 0.10.5

Parameters
----------
qe : numeric, pandas.Series or pandas.DataFrame
Quantum efficiency.
If pandas subtype, index must be the wavelength in nanometers.

wavelength : numeric, optional
Points where quantum efficiency is measured, in nanometers :math:`nm`.

Returns
-------
spectral_response : numeric, same type as ``qe``
Spectral response in :math:`A/W`.

Notes
-----
- If ``qe`` is of type ``pandas.Series`` or ``pandas.DataFrame``,
column names will remain unchanged in the returned object.
- If ``wavelength`` is provided it will be used independently of the
datatype of ``qe``.

Examples
--------
>>> import numpy as np
>>> from pvlib import spectrum
>>> wavelengths = np.array([350, 550, 750])
>>> quantum_efficiency = np.array([0.86, 0.90, 0.94])
>>> spectral_response = \
>>> spectrum.quantum_efficiency_to_spectral_responsivity(
>>> quantum_efficiency, wavelengths
>>> )
>>> spectral_response
array([0.24277287, 0.39924442, 0.56862085])

>>> quantum_efficiency_series = \
>>> pd.Series(quantum_efficiency, index=wavelengths, name="dataset")
>>> sr = spectrum.quantum_efficiency_to_spectral_responsivity(
>>> quantum_efficiency_series
>>> )
>>> sr
350 0.242773
550 0.399244
750 0.568621
Name: dataset, dtype: float64

References
----------
.. [1] “Spectral Response,” PV Performance Modeling Collaborative (PVPMC).
https://pvpmc.sandia.gov/modeling-guide/2-dc-module-iv/
effective-irradiance/spectral-response/
.. [2] “Spectral Response | PVEducation,” www.pveducation.org.
https://www.pveducation.org/pvcdrom/solar-cell-operation/
spectral-response

See Also
--------
pvlib.spectrum.spectral_responsivity_to_quantum_efficiency
"""
if wavelength is None:
if hasattr(qe, "index"): # true for pandas objects
# use reference to index values instead of index alone so
# sr / wavelength returns a series with the same name
wavelength = qe.index.array
else:
raise TypeError(
"'qe' must have an '.index' attribute"
+ " or 'wavelength' must be provided"
)
spectral_responsivity = (
qe
* wavelength
/ _PLANCK_BY_LIGHT_SPEED_OVER_ELEMENTAL_CHARGE_BY_BILLION
)
return spectral_responsivity
91 changes: 90 additions & 1 deletion pvlib/tests/test_spectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def test_get_am15g():

def test_calc_spectral_mismatch_field(spectrl2_data):
# test that the mismatch is calculated correctly with
# - default and custom reference sepctrum
# - default and custom reference spectrum
# - single or multiple sun spectra

# sample data
Expand Down Expand Up @@ -315,3 +315,92 @@ def test_spectral_factor_caballero_supplied_ambiguous():
with pytest.raises(ValueError):
spectrum.spectral_factor_caballero(1, 1, 1, module_type=None,
coefficients=None)


@pytest.fixture
def sr_and_eqe_fixture():
# Just some arbitrary data for testing the conversion functions
echedey-ls marked this conversation as resolved.
Show resolved Hide resolved
df = pd.DataFrame(
columns=("wavelength", "quantum_efficiency", "spectral_response"),
data=[
# nm, [0,1], A/W
[300, 0.85, 0.205671370402405],
[350, 0.86, 0.242772872514211],
[400, 0.87, 0.280680929019753],
[450, 0.88, 0.319395539919029],
[500, 0.89, 0.358916705212040],
[550, 0.90, 0.399244424898786],
[600, 0.91, 0.440378698979267],
[650, 0.92, 0.482319527453483],
[700, 0.93, 0.525066910321434],
[750, 0.94, 0.568620847583119],
[800, 0.95, 0.612981339238540],
[850, 0.90, 0.617014111207215],
[900, 0.80, 0.580719163489143],
[950, 0.70, 0.536358671833723],
[1000, 0.6, 0.483932636240953],
[1050, 0.4, 0.338752845368667],
],
)
df.set_index("wavelength", inplace=True)
return df


def test_spectral_responsivity_to_quantum_efficiency(sr_and_eqe_fixture):
# scalar type
qe = spectrum.spectral_responsivity_to_quantum_efficiency(
sr_and_eqe_fixture["spectral_response"].values[0],
sr_and_eqe_fixture.index.values[0], # wavelength, nm
)
assert_approx_equal(qe, sr_and_eqe_fixture["quantum_efficiency"].values[0])
# vector type
qe = spectrum.spectral_responsivity_to_quantum_efficiency(
sr_and_eqe_fixture["spectral_response"].values,
sr_and_eqe_fixture.index.values, # wavelength, nm
)
assert_allclose(qe, sr_and_eqe_fixture["quantum_efficiency"])
# pandas series type
# note: output Series' name should match the input
qe = spectrum.spectral_responsivity_to_quantum_efficiency(
sr_and_eqe_fixture["spectral_response"]
)
pd.testing.assert_series_equal(
qe, sr_and_eqe_fixture["quantum_efficiency"],
check_names=False
)
assert qe.name == "spectral_response"
# error on lack of wavelength parameter if no pandas object is provided
with pytest.raises(TypeError, match="must have an '.index' attribute"):
_ = spectrum.spectral_responsivity_to_quantum_efficiency(
sr_and_eqe_fixture["spectral_response"].values
)


def test_quantum_efficiency_to_spectral_responsivity(sr_and_eqe_fixture):
# scalar type
sr = spectrum.quantum_efficiency_to_spectral_responsivity(
sr_and_eqe_fixture["quantum_efficiency"].values[0],
sr_and_eqe_fixture.index.values[0], # wavelength, nm
)
assert_approx_equal(sr, sr_and_eqe_fixture["spectral_response"].values[0])
# vector type
sr = spectrum.quantum_efficiency_to_spectral_responsivity(
sr_and_eqe_fixture["quantum_efficiency"].values,
sr_and_eqe_fixture.index.values, # wavelength, nm
)
assert_allclose(sr, sr_and_eqe_fixture["spectral_response"])
# pandas series type
# note: output Series' name should match the input
sr = spectrum.quantum_efficiency_to_spectral_responsivity(
sr_and_eqe_fixture["quantum_efficiency"]
)
pd.testing.assert_series_equal(
sr, sr_and_eqe_fixture["spectral_response"],
check_names=False
)
assert sr.name == "quantum_efficiency"
# error on lack of wavelength parameter if no pandas object is provided
with pytest.raises(TypeError, match="must have an '.index' attribute"):
_ = spectrum.quantum_efficiency_to_spectral_responsivity(
sr_and_eqe_fixture["quantum_efficiency"].values
)
Loading