Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
2 changes: 1 addition & 1 deletion docs/sphinx/source/whatsnew/v0.11.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Enhancements
* Restructured the pvlib/spectrum folder by breaking up the contents of
pvlib/spectrum/mismatch.py into pvlib/spectrum/mismatch.py,
pvlib/spectrum/irradiance.py, and
pvlib/spectrum/response.py. (:issue:`2125`, :pull:`2136`)
pvlib/spectrum/response.py. (:issue:`2125`, :pull:`2136`, :pull:`2151`)
* Added function for calculating wind speed at different heights,
:py:func:`pvlib.atmosphere.windspeed_powerlaw`.
(:issue:`2118`, :pull:`2124`)
Expand Down
Empty file.
74 changes: 74 additions & 0 deletions pvlib/tests/spectrum/test_irradiance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import pytest
from numpy.testing import assert_allclose, assert_approx_equal, assert_equal
import pandas as pd
import numpy as np
from pvlib import spectrum
from pvlib._deprecation import pvlibDeprecationWarning

from ..conftest import assert_series_equal, fail_on_pvlib_version


@fail_on_pvlib_version('0.12')
def test_get_am15g():
# test that the reference spectrum is read and interpolated correctly
with pytest.warns(pvlibDeprecationWarning,
match="get_reference_spectra instead"):
e = spectrum.get_am15g()
assert_equal(len(e), 2002)
assert_equal(np.sum(e.index), 2761442)
assert_approx_equal(np.sum(e), 1002.88, significant=6)

wavelength = [270, 850, 950, 1200, 1201.25, 4001]
expected = [0.0, 0.893720, 0.147260, 0.448250, 0.4371025, 0.0]

with pytest.warns(pvlibDeprecationWarning,
match="get_reference_spectra instead"):
e = spectrum.get_am15g(wavelength)
assert_equal(len(e), len(wavelength))
assert_allclose(e, expected, rtol=1e-6)


@pytest.mark.parametrize(
"reference_identifier,expected_sums",
[
(
"ASTM G173-03", # reference_identifier
{ # expected_sums
"extraterrestrial": 1356.15,
"global": 1002.88,
"direct": 887.65,
},
),
],
)
def test_get_reference_spectra(reference_identifier, expected_sums):
# test reading of a standard spectrum
standard = spectrum.get_reference_spectra(standard=reference_identifier)
assert set(standard.columns) == expected_sums.keys()
assert standard.index.name == "wavelength"
assert standard.index.is_monotonic_increasing is True
expected_sums = pd.Series(expected_sums) # convert prior to comparison
assert_series_equal(np.sum(standard, axis=0), expected_sums, atol=1e-2)


def test_get_reference_spectra_custom_wavelengths():
# test that the spectrum is interpolated correctly when custom wavelengths
# are specified
# only checked for ASTM G173-03 reference spectrum
wavelength = [270, 850, 951.634, 1200, 4001]
expected_sums = pd.Series(
{"extraterrestrial": 2.23266, "global": 1.68952, "direct": 1.58480}
) # for given ``wavelength``
standard = spectrum.get_reference_spectra(
wavelength, standard="ASTM G173-03"
)
assert_equal(len(standard), len(wavelength))
# check no NaN values were returned
assert not standard.isna().any().any() # double any to return one value
assert_series_equal(np.sum(standard, axis=0), expected_sums, atol=1e-4)


def test_get_reference_spectra_invalid_reference():
# test that an invalid reference identifier raises a ValueError
with pytest.raises(ValueError, match="Invalid standard identifier"):
spectrum.get_reference_spectra(standard="invalid")
257 changes: 2 additions & 255 deletions pvlib/tests/test_spectrum.py → pvlib/tests/spectrum/test_mismatch.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import pytest
from numpy.testing import assert_allclose, assert_approx_equal, assert_equal
from numpy.testing import assert_allclose, assert_approx_equal
import pandas as pd
import numpy as np
from pvlib import spectrum
from pvlib._deprecation import pvlibDeprecationWarning

from .conftest import DATA_DIR, assert_series_equal, fail_on_pvlib_version
from ..conftest import DATA_DIR, assert_series_equal

SPECTRL2_TEST_DATA = DATA_DIR / 'spectrl2_example_spectra.csv'

Expand Down Expand Up @@ -43,154 +42,6 @@ def spectrl2_data():
return kwargs, df


def test_spectrl2(spectrl2_data):
# compare against output from solar_utils wrapper around NREL spectrl2_2.c
kwargs, expected = spectrl2_data
actual = spectrum.spectrl2(**kwargs)
assert_allclose(expected['wavelength'].values, actual['wavelength'])
assert_allclose(expected['specdif'].values, actual['dhi'].ravel(),
atol=7e-5)
assert_allclose(expected['specdir'].values, actual['dni'].ravel(),
atol=1.5e-4)
assert_allclose(expected['specetr'], actual['dni_extra'].ravel(),
atol=2e-4)
assert_allclose(expected['specglo'], actual['poa_global'].ravel(),
atol=1e-4)


def test_spectrl2_array(spectrl2_data):
# test that supplying arrays instead of scalars works
kwargs, expected = spectrl2_data
kwargs = {k: np.array([v, v, v]) for k, v in kwargs.items()}
actual = spectrum.spectrl2(**kwargs)

assert actual['wavelength'].shape == (122,)

keys = ['dni_extra', 'dhi', 'dni', 'poa_sky_diffuse', 'poa_ground_diffuse',
'poa_direct', 'poa_global']
for key in keys:
assert actual[key].shape == (122, 3)


def test_spectrl2_series(spectrl2_data):
# test that supplying Series instead of scalars works
kwargs, expected = spectrl2_data
kwargs.pop('dayofyear')
index = pd.to_datetime(['2020-03-15 10:45:59']*3)
kwargs = {k: pd.Series([v, v, v], index=index) for k, v in kwargs.items()}
actual = spectrum.spectrl2(**kwargs)

assert actual['wavelength'].shape == (122,)

keys = ['dni_extra', 'dhi', 'dni', 'poa_sky_diffuse', 'poa_ground_diffuse',
'poa_direct', 'poa_global']
for key in keys:
assert actual[key].shape == (122, 3)


def test_dayofyear_missing(spectrl2_data):
# test that not specifying dayofyear with non-pandas inputs raises error
kwargs, expected = spectrl2_data
kwargs.pop('dayofyear')
with pytest.raises(ValueError, match='dayofyear must be specified'):
_ = spectrum.spectrl2(**kwargs)


def test_aoi_gt_90(spectrl2_data):
# test that returned irradiance values are non-negative when aoi > 90
# see GH #1348
kwargs, _ = spectrl2_data
kwargs['apparent_zenith'] = 70
kwargs['aoi'] = 130
kwargs['surface_tilt'] = 60

spectra = spectrum.spectrl2(**kwargs)
for key in ['poa_direct', 'poa_global']:
message = f'{key} contains negative values for aoi>90'
assert np.all(spectra[key] >= 0), message


def test_get_example_spectral_response():
# test that the sample sr is read and interpolated correctly
sr = spectrum.get_example_spectral_response()
assert_equal(len(sr), 185)
assert_equal(np.sum(sr.index), 136900)
assert_approx_equal(np.sum(sr), 107.6116)

wavelength = [270, 850, 950, 1200, 4001]
expected = [0.0, 0.92778, 1.0, 0.0, 0.0]

sr = spectrum.get_example_spectral_response(wavelength)
assert_equal(len(sr), len(wavelength))
assert_allclose(sr, expected, rtol=1e-5)


@fail_on_pvlib_version('0.12')
def test_get_am15g():
# test that the reference spectrum is read and interpolated correctly
with pytest.warns(pvlibDeprecationWarning,
match="get_reference_spectra instead"):
e = spectrum.get_am15g()
assert_equal(len(e), 2002)
assert_equal(np.sum(e.index), 2761442)
assert_approx_equal(np.sum(e), 1002.88, significant=6)

wavelength = [270, 850, 950, 1200, 1201.25, 4001]
expected = [0.0, 0.893720, 0.147260, 0.448250, 0.4371025, 0.0]

with pytest.warns(pvlibDeprecationWarning,
match="get_reference_spectra instead"):
e = spectrum.get_am15g(wavelength)
assert_equal(len(e), len(wavelength))
assert_allclose(e, expected, rtol=1e-6)


@pytest.mark.parametrize(
"reference_identifier,expected_sums",
[
(
"ASTM G173-03", # reference_identifier
{ # expected_sums
"extraterrestrial": 1356.15,
"global": 1002.88,
"direct": 887.65,
},
),
],
)
def test_get_reference_spectra(reference_identifier, expected_sums):
# test reading of a standard spectrum
standard = spectrum.get_reference_spectra(standard=reference_identifier)
assert set(standard.columns) == expected_sums.keys()
assert standard.index.name == "wavelength"
assert standard.index.is_monotonic_increasing is True
expected_sums = pd.Series(expected_sums) # convert prior to comparison
assert_series_equal(np.sum(standard, axis=0), expected_sums, atol=1e-2)


def test_get_reference_spectra_custom_wavelengths():
# test that the spectrum is interpolated correctly when custom wavelengths
# are specified
# only checked for ASTM G173-03 reference spectrum
wavelength = [270, 850, 951.634, 1200, 4001]
expected_sums = pd.Series(
{"extraterrestrial": 2.23266, "global": 1.68952, "direct": 1.58480}
) # for given ``wavelength``
standard = spectrum.get_reference_spectra(
wavelength, standard="ASTM G173-03"
)
assert_equal(len(standard), len(wavelength))
# check no NaN values were returned
assert not standard.isna().any().any() # double any to return one value
assert_series_equal(np.sum(standard, axis=0), expected_sums, atol=1e-4)


def test_get_reference_spectra_invalid_reference():
# test that an invalid reference identifier raises a ValueError
with pytest.raises(ValueError, match="Invalid standard identifier"):
spectrum.get_reference_spectra(standard="invalid")


def test_calc_spectral_mismatch_field(spectrl2_data):
# test that the mismatch is calculated correctly with
# - default and custom reference spectrum
Expand Down Expand Up @@ -486,107 +337,3 @@ def test_spectral_factor_jrc_supplied_ambiguous():
with pytest.raises(ValueError, match='No valid input provided'):
spectrum.spectral_factor_jrc(1.0, 0.8, module_type=None,
coefficients=None)


@pytest.fixture
def sr_and_eqe_fixture():
# Just some arbitrary data for testing the conversion functions
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_sr_to_qe(sr_and_eqe_fixture):
# vector type
qe = spectrum.sr_to_qe(
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.sr_to_qe(
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"
# series normalization
qe = spectrum.sr_to_qe(
sr_and_eqe_fixture["spectral_response"] * 10, normalize=True
)
pd.testing.assert_series_equal(
qe,
sr_and_eqe_fixture["quantum_efficiency"]
/ max(sr_and_eqe_fixture["quantum_efficiency"]),
check_names=False,
)
# error on lack of wavelength parameter if no pandas object is provided
with pytest.raises(TypeError, match="must have an '.index' attribute"):
_ = spectrum.sr_to_qe(sr_and_eqe_fixture["spectral_response"].values)


def test_qe_to_sr(sr_and_eqe_fixture):
# vector type
sr = spectrum.qe_to_sr(
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.qe_to_sr(
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"
# series normalization
sr = spectrum.qe_to_sr(
sr_and_eqe_fixture["quantum_efficiency"] * 10, normalize=True
)
pd.testing.assert_series_equal(
sr,
sr_and_eqe_fixture["spectral_response"]
/ max(sr_and_eqe_fixture["spectral_response"]),
check_names=False,
)
# error on lack of wavelength parameter if no pandas object is provided
with pytest.raises(TypeError, match="must have an '.index' attribute"):
_ = spectrum.qe_to_sr(
sr_and_eqe_fixture["quantum_efficiency"].values
)


def test_qe_and_sr_reciprocal_conversion(sr_and_eqe_fixture):
# test that the conversion functions are reciprocal
qe = spectrum.sr_to_qe(sr_and_eqe_fixture["spectral_response"])
sr = spectrum.qe_to_sr(qe)
assert_allclose(sr, sr_and_eqe_fixture["spectral_response"])
qe = spectrum.sr_to_qe(sr)
assert_allclose(qe, sr_and_eqe_fixture["quantum_efficiency"])
Loading
Loading