Skip to content

Allow scalar surface orientation inputs to pvlib.bifacial.pvfactors_timeseries #1361

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 4 commits into from
Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from all 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: 2 additions & 0 deletions docs/sphinx/source/whatsnew/v0.9.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Bug fixes
values were returned when the sun is behind the plane of array (:issue:`1348`, :pull:`1349`)
* Fixed bug in :py:func:`pvlib.iotools.get_pvgis_hourly` where the ``optimal_surface_tilt``
argument was not being passed to the ``optimalinclination`` request parameter (:pull:`1356`)
* Fixed bug in :py:func:`pvlib.bifacial.pvfactors_timeseries` where scalar ``surface_tilt``
and ``surface_azimuth`` inputs caused an error (:issue:`1127`, :issue:`1332`, :pull:`1361`)


Testing
Expand Down
33 changes: 8 additions & 25 deletions pvlib/bifacial.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,31 +82,14 @@ def pvfactors_timeseries(
Bifacial PV and Diffuse Shade on Single-Axis Trackers." 44th IEEE
Photovoltaic Specialist Conference. 2017.
"""
# Convert pandas Series inputs (and some lists) to numpy arrays
if isinstance(solar_azimuth, pd.Series):
solar_azimuth = solar_azimuth.values
elif isinstance(solar_azimuth, list):
solar_azimuth = np.array(solar_azimuth)
if isinstance(solar_zenith, pd.Series):
solar_zenith = solar_zenith.values
elif isinstance(solar_zenith, list):
solar_zenith = np.array(solar_zenith)
if isinstance(surface_azimuth, pd.Series):
surface_azimuth = surface_azimuth.values
elif isinstance(surface_azimuth, list):
surface_azimuth = np.array(surface_azimuth)
if isinstance(surface_tilt, pd.Series):
surface_tilt = surface_tilt.values
elif isinstance(surface_tilt, list):
surface_tilt = np.array(surface_tilt)
if isinstance(dni, pd.Series):
dni = dni.values
elif isinstance(dni, list):
dni = np.array(dni)
if isinstance(dhi, pd.Series):
dhi = dhi.values
elif isinstance(dhi, list):
dhi = np.array(dhi)
# Convert Series, list, float inputs to numpy arrays
solar_azimuth = np.array(solar_azimuth)
solar_zenith = np.array(solar_zenith)
dni = np.array(dni)
dhi = np.array(dhi)
# GH 1127, GH 1332
surface_tilt = np.full_like(solar_zenith, surface_tilt)
surface_azimuth = np.full_like(solar_zenith, surface_azimuth)

# Import pvfactors functions for timeseries calculations.
from pvfactors.run import run_timeseries_engine
Expand Down
151 changes: 66 additions & 85 deletions pvlib/tests/test_bifacial.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,97 +5,78 @@
import pytest


@requires_pvfactors
def test_pvfactors_timeseries():
""" Test that pvfactors is functional, using the TLDR section inputs of the
package github repo README.md file:
https://github.com/SunPower/pvfactors/blob/master/README.md#tldr---quick-start"""

# Create some inputs
timestamps = pd.DatetimeIndex([datetime(2017, 8, 31, 11),
datetime(2017, 8, 31, 12)]
).set_names('timestamps')
solar_zenith = [20., 10.]
solar_azimuth = [110., 140.]
surface_tilt = [10., 0.]
surface_azimuth = [90., 90.]
axis_azimuth = 0.
dni = [1000., 300.]
dhi = [50., 500.]
gcr = 0.4
pvrow_height = 1.75
pvrow_width = 2.44
albedo = 0.2
n_pvrows = 3
index_observed_pvrow = 1
rho_front_pvrow = 0.03
rho_back_pvrow = 0.05
horizon_band_angle = 15.

# Expected values
expected_ipoa_front = pd.Series([1034.95474708997, 795.4423259036623],
index=timestamps,
name=('total_inc_front'))
expected_ipoa_back = pd.Series([92.12563846416197, 78.05831585685098],
index=timestamps,
name=('total_inc_back'))
@pytest.fixture
def example_values():
"""
Example values from the pvfactors github repo README file:
https://github.com/SunPower/pvfactors/blob/master/README.rst#quick-start
"""
inputs = dict(
timestamps=pd.DatetimeIndex([datetime(2017, 8, 31, 11),
datetime(2017, 8, 31, 12)]),
solar_zenith=[20., 10.],
solar_azimuth=[110., 140.],
surface_tilt=[10., 0.],
surface_azimuth=[90., 90.],
axis_azimuth=0.,
dni=[1000., 300.],
dhi=[50., 500.],
gcr=0.4,
pvrow_height=1.75,
pvrow_width=2.44,
albedo=0.2,
n_pvrows=3,
index_observed_pvrow=1,
rho_front_pvrow=0.03,
rho_back_pvrow=0.05,
horizon_band_angle=15.,
)
outputs = dict(
expected_ipoa_front=pd.Series([1034.95474708997, 795.4423259036623],
index=inputs['timestamps'],
name=('total_inc_front')),
expected_ipoa_back=pd.Series([92.12563846416197, 78.05831585685098],
index=inputs['timestamps'],
name=('total_inc_back')),
)
return inputs, outputs

# Run calculation
ipoa_inc_front, ipoa_inc_back, _, _ = pvfactors_timeseries(
solar_azimuth, solar_zenith, surface_azimuth, surface_tilt,
axis_azimuth,
timestamps, dni, dhi, gcr, pvrow_height, pvrow_width, albedo,
n_pvrows=n_pvrows, index_observed_pvrow=index_observed_pvrow,
rho_front_pvrow=rho_front_pvrow, rho_back_pvrow=rho_back_pvrow,
horizon_band_angle=horizon_band_angle)

assert_series_equal(ipoa_inc_front, expected_ipoa_front)
assert_series_equal(ipoa_inc_back, expected_ipoa_back)
@requires_pvfactors
def test_pvfactors_timeseries_list(example_values):
"""Test basic pvfactors functionality with list inputs"""
inputs, outputs = example_values
ipoa_inc_front, ipoa_inc_back, _, _ = pvfactors_timeseries(**inputs)
assert_series_equal(ipoa_inc_front, outputs['expected_ipoa_front'])
assert_series_equal(ipoa_inc_back, outputs['expected_ipoa_back'])


@requires_pvfactors
def test_pvfactors_timeseries_pandas_inputs():
""" Test that pvfactors is functional, using the TLDR section inputs of the
package github repo README.md file, but converted to pandas Series:
https://github.com/SunPower/pvfactors/blob/master/README.md#tldr---quick-start"""
def test_pvfactors_timeseries_pandas(example_values):
"""Test basic pvfactors functionality with Series inputs"""

inputs, outputs = example_values
for key in ['solar_zenith', 'solar_azimuth', 'surface_tilt',
'surface_azimuth', 'dni', 'dhi']:
inputs[key] = pd.Series(inputs[key], index=inputs['timestamps'])

# Create some inputs
timestamps = pd.DatetimeIndex([datetime(2017, 8, 31, 11),
datetime(2017, 8, 31, 12)]
).set_names('timestamps')
solar_zenith = pd.Series([20., 10.])
solar_azimuth = pd.Series([110., 140.])
surface_tilt = pd.Series([10., 0.])
surface_azimuth = pd.Series([90., 90.])
axis_azimuth = 0.
dni = pd.Series([1000., 300.])
dhi = pd.Series([50., 500.])
gcr = 0.4
pvrow_height = 1.75
pvrow_width = 2.44
albedo = 0.2
n_pvrows = 3
index_observed_pvrow = 1
rho_front_pvrow = 0.03
rho_back_pvrow = 0.05
horizon_band_angle = 15.
ipoa_inc_front, ipoa_inc_back, _, _ = pvfactors_timeseries(**inputs)
assert_series_equal(ipoa_inc_front, outputs['expected_ipoa_front'])
assert_series_equal(ipoa_inc_back, outputs['expected_ipoa_back'])

# Expected values
expected_ipoa_front = pd.Series([1034.95474708997, 795.4423259036623],
index=timestamps,
name=('total_inc_front'))
expected_ipoa_back = pd.Series([92.12563846416197, 78.05831585685098],
index=timestamps,
name=('total_inc_back'))

# Run calculation
ipoa_inc_front, ipoa_inc_back, _, _ = pvfactors_timeseries(
solar_azimuth, solar_zenith, surface_azimuth, surface_tilt,
axis_azimuth,
timestamps, dni, dhi, gcr, pvrow_height, pvrow_width, albedo,
n_pvrows=n_pvrows, index_observed_pvrow=index_observed_pvrow,
rho_front_pvrow=rho_front_pvrow, rho_back_pvrow=rho_back_pvrow,
horizon_band_angle=horizon_band_angle)
@requires_pvfactors
def test_pvfactors_scalar_orientation(example_values):
"""test that surface_tilt and surface_azimuth inputs can be scalars"""
# GH 1127, GH 1332
inputs, outputs = example_values
inputs['surface_tilt'] = 10.
inputs['surface_azimuth'] = 90.
# the second tilt is supposed to be zero, so we need to
# update the expected irradiances too:
outputs['expected_ipoa_front'].iloc[1] = 800.6524022701132
outputs['expected_ipoa_back'].iloc[1] = 81.72135884745822

assert_series_equal(ipoa_inc_front, expected_ipoa_front)
assert_series_equal(ipoa_inc_back, expected_ipoa_back)
ipoa_inc_front, ipoa_inc_back, _, _ = pvfactors_timeseries(**inputs)
assert_series_equal(ipoa_inc_front, outputs['expected_ipoa_front'])
assert_series_equal(ipoa_inc_back, outputs['expected_ipoa_back'])