Skip to content

Commit 1ab0eb2

Browse files
authored
Allow scalar surface orientation inputs to pvlib.bifacial.pvfactors_timeseries (#1361)
* simplify pvfactors_timeseries, fix scalar bug, rework tests * whatsnew * remove overlooked debugging code * nix is_scalar conditional, use full_like instead
1 parent 2516c96 commit 1ab0eb2

File tree

3 files changed

+76
-110
lines changed

3 files changed

+76
-110
lines changed

docs/sphinx/source/whatsnew/v0.9.1.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Bug fixes
2020
values were returned when the sun is behind the plane of array (:issue:`1348`, :pull:`1349`)
2121
* Fixed bug in :py:func:`pvlib.iotools.get_pvgis_hourly` where the ``optimal_surface_tilt``
2222
argument was not being passed to the ``optimalinclination`` request parameter (:pull:`1356`)
23+
* Fixed bug in :py:func:`pvlib.bifacial.pvfactors_timeseries` where scalar ``surface_tilt``
24+
and ``surface_azimuth`` inputs caused an error (:issue:`1127`, :issue:`1332`, :pull:`1361`)
2325

2426

2527
Testing

pvlib/bifacial.py

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -82,31 +82,14 @@ def pvfactors_timeseries(
8282
Bifacial PV and Diffuse Shade on Single-Axis Trackers." 44th IEEE
8383
Photovoltaic Specialist Conference. 2017.
8484
"""
85-
# Convert pandas Series inputs (and some lists) to numpy arrays
86-
if isinstance(solar_azimuth, pd.Series):
87-
solar_azimuth = solar_azimuth.values
88-
elif isinstance(solar_azimuth, list):
89-
solar_azimuth = np.array(solar_azimuth)
90-
if isinstance(solar_zenith, pd.Series):
91-
solar_zenith = solar_zenith.values
92-
elif isinstance(solar_zenith, list):
93-
solar_zenith = np.array(solar_zenith)
94-
if isinstance(surface_azimuth, pd.Series):
95-
surface_azimuth = surface_azimuth.values
96-
elif isinstance(surface_azimuth, list):
97-
surface_azimuth = np.array(surface_azimuth)
98-
if isinstance(surface_tilt, pd.Series):
99-
surface_tilt = surface_tilt.values
100-
elif isinstance(surface_tilt, list):
101-
surface_tilt = np.array(surface_tilt)
102-
if isinstance(dni, pd.Series):
103-
dni = dni.values
104-
elif isinstance(dni, list):
105-
dni = np.array(dni)
106-
if isinstance(dhi, pd.Series):
107-
dhi = dhi.values
108-
elif isinstance(dhi, list):
109-
dhi = np.array(dhi)
85+
# Convert Series, list, float inputs to numpy arrays
86+
solar_azimuth = np.array(solar_azimuth)
87+
solar_zenith = np.array(solar_zenith)
88+
dni = np.array(dni)
89+
dhi = np.array(dhi)
90+
# GH 1127, GH 1332
91+
surface_tilt = np.full_like(solar_zenith, surface_tilt)
92+
surface_azimuth = np.full_like(solar_zenith, surface_azimuth)
11093

11194
# Import pvfactors functions for timeseries calculations.
11295
from pvfactors.run import run_timeseries_engine

pvlib/tests/test_bifacial.py

Lines changed: 66 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -5,97 +5,78 @@
55
import pytest
66

77

8-
@requires_pvfactors
9-
def test_pvfactors_timeseries():
10-
""" Test that pvfactors is functional, using the TLDR section inputs of the
11-
package github repo README.md file:
12-
https://github.com/SunPower/pvfactors/blob/master/README.md#tldr---quick-start"""
13-
14-
# Create some inputs
15-
timestamps = pd.DatetimeIndex([datetime(2017, 8, 31, 11),
16-
datetime(2017, 8, 31, 12)]
17-
).set_names('timestamps')
18-
solar_zenith = [20., 10.]
19-
solar_azimuth = [110., 140.]
20-
surface_tilt = [10., 0.]
21-
surface_azimuth = [90., 90.]
22-
axis_azimuth = 0.
23-
dni = [1000., 300.]
24-
dhi = [50., 500.]
25-
gcr = 0.4
26-
pvrow_height = 1.75
27-
pvrow_width = 2.44
28-
albedo = 0.2
29-
n_pvrows = 3
30-
index_observed_pvrow = 1
31-
rho_front_pvrow = 0.03
32-
rho_back_pvrow = 0.05
33-
horizon_band_angle = 15.
34-
35-
# Expected values
36-
expected_ipoa_front = pd.Series([1034.95474708997, 795.4423259036623],
37-
index=timestamps,
38-
name=('total_inc_front'))
39-
expected_ipoa_back = pd.Series([92.12563846416197, 78.05831585685098],
40-
index=timestamps,
41-
name=('total_inc_back'))
8+
@pytest.fixture
9+
def example_values():
10+
"""
11+
Example values from the pvfactors github repo README file:
12+
https://github.com/SunPower/pvfactors/blob/master/README.rst#quick-start
13+
"""
14+
inputs = dict(
15+
timestamps=pd.DatetimeIndex([datetime(2017, 8, 31, 11),
16+
datetime(2017, 8, 31, 12)]),
17+
solar_zenith=[20., 10.],
18+
solar_azimuth=[110., 140.],
19+
surface_tilt=[10., 0.],
20+
surface_azimuth=[90., 90.],
21+
axis_azimuth=0.,
22+
dni=[1000., 300.],
23+
dhi=[50., 500.],
24+
gcr=0.4,
25+
pvrow_height=1.75,
26+
pvrow_width=2.44,
27+
albedo=0.2,
28+
n_pvrows=3,
29+
index_observed_pvrow=1,
30+
rho_front_pvrow=0.03,
31+
rho_back_pvrow=0.05,
32+
horizon_band_angle=15.,
33+
)
34+
outputs = dict(
35+
expected_ipoa_front=pd.Series([1034.95474708997, 795.4423259036623],
36+
index=inputs['timestamps'],
37+
name=('total_inc_front')),
38+
expected_ipoa_back=pd.Series([92.12563846416197, 78.05831585685098],
39+
index=inputs['timestamps'],
40+
name=('total_inc_back')),
41+
)
42+
return inputs, outputs
4243

43-
# Run calculation
44-
ipoa_inc_front, ipoa_inc_back, _, _ = pvfactors_timeseries(
45-
solar_azimuth, solar_zenith, surface_azimuth, surface_tilt,
46-
axis_azimuth,
47-
timestamps, dni, dhi, gcr, pvrow_height, pvrow_width, albedo,
48-
n_pvrows=n_pvrows, index_observed_pvrow=index_observed_pvrow,
49-
rho_front_pvrow=rho_front_pvrow, rho_back_pvrow=rho_back_pvrow,
50-
horizon_band_angle=horizon_band_angle)
5144

52-
assert_series_equal(ipoa_inc_front, expected_ipoa_front)
53-
assert_series_equal(ipoa_inc_back, expected_ipoa_back)
45+
@requires_pvfactors
46+
def test_pvfactors_timeseries_list(example_values):
47+
"""Test basic pvfactors functionality with list inputs"""
48+
inputs, outputs = example_values
49+
ipoa_inc_front, ipoa_inc_back, _, _ = pvfactors_timeseries(**inputs)
50+
assert_series_equal(ipoa_inc_front, outputs['expected_ipoa_front'])
51+
assert_series_equal(ipoa_inc_back, outputs['expected_ipoa_back'])
5452

5553

5654
@requires_pvfactors
57-
def test_pvfactors_timeseries_pandas_inputs():
58-
""" Test that pvfactors is functional, using the TLDR section inputs of the
59-
package github repo README.md file, but converted to pandas Series:
60-
https://github.com/SunPower/pvfactors/blob/master/README.md#tldr---quick-start"""
55+
def test_pvfactors_timeseries_pandas(example_values):
56+
"""Test basic pvfactors functionality with Series inputs"""
57+
58+
inputs, outputs = example_values
59+
for key in ['solar_zenith', 'solar_azimuth', 'surface_tilt',
60+
'surface_azimuth', 'dni', 'dhi']:
61+
inputs[key] = pd.Series(inputs[key], index=inputs['timestamps'])
6162

62-
# Create some inputs
63-
timestamps = pd.DatetimeIndex([datetime(2017, 8, 31, 11),
64-
datetime(2017, 8, 31, 12)]
65-
).set_names('timestamps')
66-
solar_zenith = pd.Series([20., 10.])
67-
solar_azimuth = pd.Series([110., 140.])
68-
surface_tilt = pd.Series([10., 0.])
69-
surface_azimuth = pd.Series([90., 90.])
70-
axis_azimuth = 0.
71-
dni = pd.Series([1000., 300.])
72-
dhi = pd.Series([50., 500.])
73-
gcr = 0.4
74-
pvrow_height = 1.75
75-
pvrow_width = 2.44
76-
albedo = 0.2
77-
n_pvrows = 3
78-
index_observed_pvrow = 1
79-
rho_front_pvrow = 0.03
80-
rho_back_pvrow = 0.05
81-
horizon_band_angle = 15.
63+
ipoa_inc_front, ipoa_inc_back, _, _ = pvfactors_timeseries(**inputs)
64+
assert_series_equal(ipoa_inc_front, outputs['expected_ipoa_front'])
65+
assert_series_equal(ipoa_inc_back, outputs['expected_ipoa_back'])
8266

83-
# Expected values
84-
expected_ipoa_front = pd.Series([1034.95474708997, 795.4423259036623],
85-
index=timestamps,
86-
name=('total_inc_front'))
87-
expected_ipoa_back = pd.Series([92.12563846416197, 78.05831585685098],
88-
index=timestamps,
89-
name=('total_inc_back'))
9067

91-
# Run calculation
92-
ipoa_inc_front, ipoa_inc_back, _, _ = pvfactors_timeseries(
93-
solar_azimuth, solar_zenith, surface_azimuth, surface_tilt,
94-
axis_azimuth,
95-
timestamps, dni, dhi, gcr, pvrow_height, pvrow_width, albedo,
96-
n_pvrows=n_pvrows, index_observed_pvrow=index_observed_pvrow,
97-
rho_front_pvrow=rho_front_pvrow, rho_back_pvrow=rho_back_pvrow,
98-
horizon_band_angle=horizon_band_angle)
68+
@requires_pvfactors
69+
def test_pvfactors_scalar_orientation(example_values):
70+
"""test that surface_tilt and surface_azimuth inputs can be scalars"""
71+
# GH 1127, GH 1332
72+
inputs, outputs = example_values
73+
inputs['surface_tilt'] = 10.
74+
inputs['surface_azimuth'] = 90.
75+
# the second tilt is supposed to be zero, so we need to
76+
# update the expected irradiances too:
77+
outputs['expected_ipoa_front'].iloc[1] = 800.6524022701132
78+
outputs['expected_ipoa_back'].iloc[1] = 81.72135884745822
9979

100-
assert_series_equal(ipoa_inc_front, expected_ipoa_front)
101-
assert_series_equal(ipoa_inc_back, expected_ipoa_back)
80+
ipoa_inc_front, ipoa_inc_back, _, _ = pvfactors_timeseries(**inputs)
81+
assert_series_equal(ipoa_inc_front, outputs['expected_ipoa_front'])
82+
assert_series_equal(ipoa_inc_back, outputs['expected_ipoa_back'])

0 commit comments

Comments
 (0)