Skip to content

Commit 7b6cf7a

Browse files
anomamwholmgren
authored andcommitted
Update pvfactors to v1.0.1 (#722)
* Update function and tests to work with new pvfactors version 101 * Update whatsnew file * Update pvfactors version in circleci tests * Move comment to API section of whatsnew * Fix docstrings about -1 value for number of workers * Parametrize bifacial tests on running parallel calculations
1 parent 84818c6 commit 7b6cf7a

File tree

7 files changed

+104
-77
lines changed

7 files changed

+104
-77
lines changed

ci/requirements-py27.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ dependencies:
2020
- coveralls
2121
- pytest-mock
2222
- pytest-timeout
23-
- pvfactors==0.1.5
23+
- pvfactors==1.0.1

ci/requirements-py35.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ dependencies:
2020
- coveralls
2121
- pytest-mock
2222
- pytest-timeout
23-
- pvfactors==0.1.5
23+
- pvfactors==1.0.1

ci/requirements-py36.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ dependencies:
2020
- coveralls
2121
- pytest-mock
2222
- pytest-timeout
23-
- pvfactors==0.1.5
23+
- pvfactors==1.0.1

ci/requirements-py37.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ dependencies:
2020
- coveralls
2121
- pytest-mock
2222
- pytest-timeout
23-
- pvfactors==0.1.5
23+
- pvfactors==1.0.1

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ API Changes
2929
* Remove automatic column name mapping from :py:func:`~pvlib.iotools.read_midc`
3030
and :py:func:`~pvlib.iotools.read_midc_raw_data_from_nrel` and added
3131
optional keyword argument `variable_map` to map columns.
32+
* Update :py:func:`~pvlib.bifacial.pvfactors_timeseries` and tests to use
33+
``pvfactors`` v1.0.1 (:issue:`699`)
3234

3335
Enhancements
3436
~~~~~~~~~~~~
@@ -81,3 +83,4 @@ Contributors
8183
* :ghuser:`yxh289`
8284
* Jonathan Gaffiot (:ghuser:`jgaffiot`)
8385
* Leland Boeman (:ghuser: `lboeman`)
86+
* Marc Anoma (:ghuser: `anomam`)

pvlib/bifacial.py

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44
"""
55

66
import pandas as pd
7+
import numpy as np
78

89

910
def pvfactors_timeseries(
1011
solar_azimuth, solar_zenith, surface_azimuth, surface_tilt,
12+
axis_azimuth,
1113
timestamps, dni, dhi, gcr, pvrow_height, pvrow_width, albedo,
1214
n_pvrows=3, index_observed_pvrow=1,
1315
rho_front_pvrow=0.03, rho_back_pvrow=0.05,
1416
horizon_band_angle=15.,
15-
run_parallel_calculations=True, n_workers_for_parallel_calcs=None):
17+
run_parallel_calculations=True, n_workers_for_parallel_calcs=2):
1618
"""
1719
Calculate front and back surface plane-of-array irradiance on
1820
a fixed tilt or single-axis tracker PV array configuration, and using
@@ -31,6 +33,9 @@ def pvfactors_timeseries(
3133
convention (deg)
3234
surface_tilt: numeric
3335
Tilt angle of the PV modules, going from 0 to 180 (deg)
36+
axis_azimuth: float
37+
Azimuth angle of the rotation axis of the PV modules, using pvlib's
38+
convention (deg). This is supposed to be fixed for all timestamps.
3439
timestamps: datetime or DatetimeIndex
3540
List of simulation timestamps
3641
dni: numeric
@@ -59,9 +64,9 @@ def pvfactors_timeseries(
5964
run_parallel_calculations: bool, default True
6065
pvfactors is capable of using multiprocessing. Use this flag to decide
6166
to run calculations in parallel (recommended) or not.
62-
n_workers_for_parallel_calcs: int, default None
67+
n_workers_for_parallel_calcs: int, default 2
6368
Number of workers to use in the case of parallel calculations. The
64-
default value of 'None' will lead to using a value equal to the number
69+
'-1' value will lead to using a value equal to the number
6570
of CPU's on the machine running the model.
6671
6772
Returns
@@ -86,63 +91,99 @@ def pvfactors_timeseries(
8691
Photovoltaic Specialist Conference. 2017.
8792
"""
8893

89-
# Convert pandas Series inputs to numpy arrays
94+
# Convert pandas Series inputs (and some lists) to numpy arrays
9095
if isinstance(solar_azimuth, pd.Series):
9196
solar_azimuth = solar_azimuth.values
97+
elif isinstance(solar_azimuth, list):
98+
solar_azimuth = np.array(solar_azimuth)
9299
if isinstance(solar_zenith, pd.Series):
93100
solar_zenith = solar_zenith.values
94101
if isinstance(surface_azimuth, pd.Series):
95102
surface_azimuth = surface_azimuth.values
103+
elif isinstance(surface_azimuth, list):
104+
surface_azimuth = np.array(surface_azimuth)
96105
if isinstance(surface_tilt, pd.Series):
97106
surface_tilt = surface_tilt.values
98107
if isinstance(dni, pd.Series):
99108
dni = dni.values
100109
if isinstance(dhi, pd.Series):
101110
dhi = dhi.values
111+
if isinstance(solar_azimuth, list):
112+
solar_azimuth = np.array(solar_azimuth)
102113

103114
# Import pvfactors functions for timeseries calculations.
104-
from pvfactors.timeseries import (calculate_radiosities_parallel_perez,
105-
calculate_radiosities_serially_perez,
106-
get_average_pvrow_outputs)
107-
idx_slice = pd.IndexSlice
115+
from pvfactors.run import (run_timeseries_engine,
116+
run_parallel_engine)
108117

109118
# Build up pv array configuration parameters
110119
pvarray_parameters = {
111120
'n_pvrows': n_pvrows,
121+
'axis_azimuth': axis_azimuth,
112122
'pvrow_height': pvrow_height,
113123
'pvrow_width': pvrow_width,
114124
'gcr': gcr,
115-
'rho_ground': albedo,
116125
'rho_front_pvrow': rho_front_pvrow,
117126
'rho_back_pvrow': rho_back_pvrow,
118127
'horizon_band_angle': horizon_band_angle
119128
}
120129

121130
# Run pvfactors calculations: either in parallel or serially
122131
if run_parallel_calculations:
123-
df_registries, df_custom_perez = calculate_radiosities_parallel_perez(
124-
pvarray_parameters, timestamps, solar_zenith, solar_azimuth,
125-
surface_tilt, surface_azimuth, dni, dhi,
126-
n_processes=n_workers_for_parallel_calcs)
132+
report = run_parallel_engine(
133+
PVFactorsReportBuilder, pvarray_parameters,
134+
timestamps, dni, dhi,
135+
solar_zenith, solar_azimuth,
136+
surface_tilt, surface_azimuth,
137+
albedo, n_processes=n_workers_for_parallel_calcs)
127138
else:
128-
inputs = (pvarray_parameters, timestamps, solar_zenith, solar_azimuth,
129-
surface_tilt, surface_azimuth, dni, dhi)
130-
df_registries, df_custom_perez = calculate_radiosities_serially_perez(
131-
inputs)
139+
report = run_timeseries_engine(
140+
PVFactorsReportBuilder.build, pvarray_parameters,
141+
timestamps, dni, dhi,
142+
solar_zenith, solar_azimuth,
143+
surface_tilt, surface_azimuth,
144+
albedo)
132145

133-
# Get the average surface outputs
134-
df_outputs = get_average_pvrow_outputs(df_registries,
135-
values=['qinc'],
136-
include_shading=True)
146+
# Turn report into dataframe
147+
df_report = pd.DataFrame(report, index=timestamps)
137148

138-
# Select the calculated outputs from the pvrow to observe
139-
ipoa_front = df_outputs.loc[:, idx_slice[index_observed_pvrow,
140-
'front', 'qinc']]
149+
return df_report.total_inc_front, df_report.total_inc_back
141150

142-
ipoa_back = df_outputs.loc[:, idx_slice[index_observed_pvrow,
143-
'back', 'qinc']]
144151

145-
# Set timestamps as index of df_registries for consistency of outputs
146-
df_registries = df_registries.set_index('timestamps')
152+
class PVFactorsReportBuilder(object):
153+
"""In pvfactors, a class is required to build reports when running
154+
calculations with multiprocessing because of python constraints"""
147155

148-
return ipoa_front, ipoa_back, df_registries
156+
@staticmethod
157+
def build(report, pvarray):
158+
"""Reports will have total incident irradiance on front and
159+
back surface of center pvrow (index=1)"""
160+
# Initialize the report as a dictionary
161+
if report is None:
162+
list_keys = ['total_inc_back', 'total_inc_front']
163+
report = {key: [] for key in list_keys}
164+
# Add elements to the report
165+
if pvarray is not None:
166+
pvrow = pvarray.pvrows[1] # use center pvrow
167+
report['total_inc_back'].append(
168+
pvrow.back.get_param_weighted('qinc'))
169+
report['total_inc_front'].append(
170+
pvrow.front.get_param_weighted('qinc'))
171+
else:
172+
# No calculation is performed when the sun is down
173+
report['total_inc_back'].append(np.nan)
174+
report['total_inc_front'].append(np.nan)
175+
176+
return report
177+
178+
@staticmethod
179+
def merge(reports):
180+
"""Works for dictionary reports"""
181+
report = reports[0]
182+
# Merge only if more than 1 report
183+
if len(reports) > 1:
184+
keys_report = list(reports[0].keys())
185+
for other_report in reports[1:]:
186+
if other_report is not None:
187+
for key in keys_report:
188+
report[key] += other_report[key]
189+
return report

pvlib/test/test_bifacial.py

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
from datetime import datetime
33
from pvlib.bifacial import pvfactors_timeseries
44
from conftest import requires_pvfactors
5+
import pytest
56

67

78
@requires_pvfactors
8-
def test_pvfactors_timeseries():
9+
@pytest.mark.parametrize('run_parallel_calculations',
10+
[False, True])
11+
def test_pvfactors_timeseries(run_parallel_calculations):
912
""" Test that pvfactors is functional, using the TLDR section inputs of the
1013
package github repo README.md file:
1114
https://github.com/SunPower/pvfactors/blob/master/README.md#tldr---quick-start"""
@@ -18,6 +21,7 @@ def test_pvfactors_timeseries():
1821
solar_azimuth = [110., 140.]
1922
surface_tilt = [10., 0.]
2023
surface_azimuth = [90., 90.]
24+
axis_azimuth = 0.
2125
dni = [1000., 300.]
2226
dhi = [50., 500.]
2327
gcr = 0.4
@@ -31,42 +35,32 @@ def test_pvfactors_timeseries():
3135
horizon_band_angle = 15.
3236

3337
# Expected values
34-
expected_ipoa_front = pd.Series([1034.96216923, 795.4423259],
38+
expected_ipoa_front = pd.Series([1034.95474708997, 795.4423259036623],
3539
index=timestamps,
36-
name=(1, 'front', 'qinc'))
37-
expected_ipoa_back = pd.Series([92.11871485, 70.39404124],
40+
name=('total_inc_front'))
41+
expected_ipoa_back = pd.Series([91.88707460262768, 78.05831585685215],
3842
index=timestamps,
39-
name=(1, 'back', 'qinc'))
43+
name=('total_inc_back'))
4044

41-
# Test serial calculations
42-
ipoa_front, ipoa_back, df_registries = pvfactors_timeseries(
45+
# Run calculation
46+
ipoa_front, ipoa_back = pvfactors_timeseries(
4347
solar_azimuth, solar_zenith, surface_azimuth, surface_tilt,
48+
axis_azimuth,
4449
timestamps, dni, dhi, gcr, pvrow_height, pvrow_width, albedo,
4550
n_pvrows=n_pvrows, index_observed_pvrow=index_observed_pvrow,
4651
rho_front_pvrow=rho_front_pvrow, rho_back_pvrow=rho_back_pvrow,
4752
horizon_band_angle=horizon_band_angle,
48-
run_parallel_calculations=False, n_workers_for_parallel_calcs=None)
53+
run_parallel_calculations=run_parallel_calculations,
54+
n_workers_for_parallel_calcs=-1)
4955

5056
pd.testing.assert_series_equal(ipoa_front, expected_ipoa_front)
5157
pd.testing.assert_series_equal(ipoa_back, expected_ipoa_back)
52-
pd.testing.assert_index_equal(timestamps, df_registries.index.unique())
53-
54-
# Run calculations in parallel
55-
ipoa_front, ipoa_back, df_registries = pvfactors_timeseries(
56-
solar_azimuth, solar_zenith, surface_azimuth, surface_tilt,
57-
timestamps, dni, dhi, gcr, pvrow_height, pvrow_width, albedo,
58-
n_pvrows=n_pvrows, index_observed_pvrow=index_observed_pvrow,
59-
rho_front_pvrow=rho_front_pvrow, rho_back_pvrow=rho_back_pvrow,
60-
horizon_band_angle=horizon_band_angle,
61-
run_parallel_calculations=True, n_workers_for_parallel_calcs=None)
62-
63-
pd.testing.assert_series_equal(ipoa_front, expected_ipoa_front)
64-
pd.testing.assert_series_equal(ipoa_back, expected_ipoa_back)
65-
pd.testing.assert_index_equal(timestamps, df_registries.index.unique())
6658

6759

6860
@requires_pvfactors
69-
def test_pvfactors_timeseries_pandas_inputs():
61+
@pytest.mark.parametrize('run_parallel_calculations',
62+
[False, True])
63+
def test_pvfactors_timeseries_pandas_inputs(run_parallel_calculations):
7064
""" Test that pvfactors is functional, using the TLDR section inputs of the
7165
package github repo README.md file, but converted to pandas Series:
7266
https://github.com/SunPower/pvfactors/blob/master/README.md#tldr---quick-start"""
@@ -79,6 +73,7 @@ def test_pvfactors_timeseries_pandas_inputs():
7973
solar_azimuth = pd.Series([110., 140.])
8074
surface_tilt = pd.Series([10., 0.])
8175
surface_azimuth = pd.Series([90., 90.])
76+
axis_azimuth = 0.
8277
dni = pd.Series([1000., 300.])
8378
dhi = pd.Series([50., 500.])
8479
gcr = 0.4
@@ -92,35 +87,23 @@ def test_pvfactors_timeseries_pandas_inputs():
9287
horizon_band_angle = 15.
9388

9489
# Expected values
95-
expected_ipoa_front = pd.Series([1034.96216923, 795.4423259],
90+
expected_ipoa_front = pd.Series([1034.95474708997, 795.4423259036623],
9691
index=timestamps,
97-
name=(1, 'front', 'qinc'))
98-
expected_ipoa_back = pd.Series([92.11871485, 70.39404124],
92+
name=('total_inc_front'))
93+
expected_ipoa_back = pd.Series([91.88707460262768, 78.05831585685215],
9994
index=timestamps,
100-
name=(1, 'back', 'qinc'))
101-
102-
# Test serial calculations
103-
ipoa_front, ipoa_back, df_registries = pvfactors_timeseries(
104-
solar_azimuth, solar_zenith, surface_azimuth, surface_tilt,
105-
timestamps, dni, dhi, gcr, pvrow_height, pvrow_width, albedo,
106-
n_pvrows=n_pvrows, index_observed_pvrow=index_observed_pvrow,
107-
rho_front_pvrow=rho_front_pvrow, rho_back_pvrow=rho_back_pvrow,
108-
horizon_band_angle=horizon_band_angle,
109-
run_parallel_calculations=False, n_workers_for_parallel_calcs=None)
110-
111-
pd.testing.assert_series_equal(ipoa_front, expected_ipoa_front)
112-
pd.testing.assert_series_equal(ipoa_back, expected_ipoa_back)
113-
pd.testing.assert_index_equal(timestamps, df_registries.index.unique())
95+
name=('total_inc_back'))
11496

115-
# Run calculations in parallel
116-
ipoa_front, ipoa_back, df_registries = pvfactors_timeseries(
97+
# Run calculation
98+
ipoa_front, ipoa_back = pvfactors_timeseries(
11799
solar_azimuth, solar_zenith, surface_azimuth, surface_tilt,
100+
axis_azimuth,
118101
timestamps, dni, dhi, gcr, pvrow_height, pvrow_width, albedo,
119102
n_pvrows=n_pvrows, index_observed_pvrow=index_observed_pvrow,
120103
rho_front_pvrow=rho_front_pvrow, rho_back_pvrow=rho_back_pvrow,
121104
horizon_band_angle=horizon_band_angle,
122-
run_parallel_calculations=True, n_workers_for_parallel_calcs=None)
105+
run_parallel_calculations=run_parallel_calculations,
106+
n_workers_for_parallel_calcs=-1)
123107

124108
pd.testing.assert_series_equal(ipoa_front, expected_ipoa_front)
125109
pd.testing.assert_series_equal(ipoa_back, expected_ipoa_back)
126-
pd.testing.assert_index_equal(timestamps, df_registries.index.unique())

0 commit comments

Comments
 (0)