"""
The ``response`` module in the ``spectrum`` package provides functions for
spectral response and quantum efficiency calculations.
"""
from pvlib.tools import normalize_max2one
import numpy as np
import pandas as pd
import scipy.constants
from scipy.interpolate import interp1d
_PLANCK_BY_LIGHT_SPEED_OVER_ELEMENTAL_CHARGE_BY_BILLION = (
scipy.constants.speed_of_light
* scipy.constants.Planck
/ scipy.constants.elementary_charge
* 1e9
)
[docs]
def get_example_spectral_response(wavelength=None):
'''
Generate a generic smooth spectral response (SR) for tests and experiments.
Parameters
----------
wavelength: 1-D sequence of numeric, optional
Wavelengths at which spectral response values are generated.
By default ``wavelength`` is from 280 to 1200 in 5 nm intervals. [nm]
Returns
-------
spectral_response : pandas.Series
The relative spectral response indexed by ``wavelength`` in nm. [-]
Notes
-----
This spectral response is based on measurements taken on a c-Si cell.
A small number of points near the measured curve are used to define
a cubic spline having no undue oscillations, as shown in [1]_. The spline
can be interpolated at arbitrary wavelengths to produce a continuous,
smooth curve , which makes it suitable for experimenting with spectral
data of different resolutions.
References
----------
.. [1] Driesse, Anton, and Stein, Joshua. "Global Normal Spectral
Irradiance in Albuquerque: a One-Year Open Dataset for PV Research".
United States 2020. :doi:`10.2172/1814068`.
'''
# Contributed by Anton Driesse (@adriesse), PV Performance Labs. Aug. 2022
SR_DATA = np.array([[290, 0.00],
[350, 0.27],
[400, 0.37],
[500, 0.52],
[650, 0.71],
[800, 0.88],
[900, 0.97],
[950, 1.00],
[1000, 0.93],
[1050, 0.58],
[1100, 0.21],
[1150, 0.05],
[1190, 0.00]]).transpose()
if wavelength is None:
resolution = 5.0
wavelength = np.arange(280, 1200 + resolution, resolution)
interpolator = interp1d(SR_DATA[0], SR_DATA[1],
kind='cubic',
bounds_error=False,
fill_value=0.0,
copy=False,
assume_sorted=True)
sr = pd.Series(data=interpolator(wavelength), index=wavelength)
sr.index.name = 'wavelength'
sr.name = 'spectral_response'
return sr
[docs]
def sr_to_qe(sr, wavelength=None, normalize=False):
"""
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, [nm].
Conversion is described in [1]_.
.. versionadded:: 0.11.0
Parameters
----------
sr : numeric, pandas.Series or pandas.DataFrame
Spectral response, [A/W].
Index must be the wavelength in nanometers, [nm].
wavelength : numeric, optional
Points where spectral response is measured, in nanometers, [nm].
normalize : bool, default False
If True, the quantum efficiency is normalized so that the maximum value
is 1.
For ``pandas.DataFrame``, normalization is done for each column.
For 2D arrays, normalization is done for each sub-array.
Returns
-------
quantum_efficiency : numeric, same type as ``sr``
Quantum efficiency, in the interval [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
>>> import pandas as pd
>>> from pvlib import spectrum
>>> wavelengths = np.array([350, 550, 750])
>>> spectral_response = np.array([0.25, 0.40, 0.57])
>>> quantum_efficiency = spectrum.sr_to_qe(spectral_response, wavelengths)
>>> print(quantum_efficiency)
array([0.88560142, 0.90170326, 0.94227991])
>>> spectral_response_series = pd.Series(spectral_response, index=wavelengths, name="dataset")
>>> qe = spectrum.sr_to_qe(spectral_response_series)
>>> print(qe)
350 0.885601
550 0.901703
750 0.942280
Name: dataset, dtype: float64
>>> qe = spectrum.sr_to_qe(spectral_response_series, normalize=True)
>>> print(qe)
350 0.939850
550 0.956938
750 1.000000
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.qe_to_sr
""" # noqa: E501
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
)
if normalize:
quantum_efficiency = normalize_max2one(quantum_efficiency)
return quantum_efficiency
[docs]
def qe_to_sr(qe, wavelength=None, normalize=False):
"""
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, [nm].
Conversion is described in [1]_.
.. versionadded:: 0.11.0
Parameters
----------
qe : numeric, pandas.Series or pandas.DataFrame
Quantum efficiency.
If pandas subtype, index must be the wavelength in nanometers, [nm].
wavelength : numeric, optional
Points where quantum efficiency is measured, in nanometers, [nm].
normalize : bool, default False
If True, the spectral response is normalized so that the maximum value
is 1.
For ``pandas.DataFrame``, normalization is done for each column.
For 2D arrays, normalization is done for each sub-array.
Returns
-------
spectral_response : numeric, same type as ``qe``
Spectral response, [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
>>> import pandas as pd
>>> from pvlib import spectrum
>>> wavelengths = np.array([350, 550, 750])
>>> quantum_efficiency = np.array([0.86, 0.90, 0.94])
>>> spectral_response = spectrum.qe_to_sr(quantum_efficiency, wavelengths)
>>> print(spectral_response)
array([0.24277287, 0.39924442, 0.56862085])
>>> quantum_efficiency_series = pd.Series(quantum_efficiency, index=wavelengths, name="dataset")
>>> sr = spectrum.qe_to_sr(quantum_efficiency_series)
>>> print(sr)
350 0.242773
550 0.399244
750 0.568621
Name: dataset, dtype: float64
>>> sr = spectrum.qe_to_sr(quantum_efficiency_series, normalize=True)
>>> print(sr)
350 0.426950
550 0.702128
750 1.000000
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.sr_to_qe
""" # noqa: E501
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
)
if normalize:
spectral_responsivity = normalize_max2one(spectral_responsivity)
return spectral_responsivity