Skip to content

Commit 1c02806

Browse files
authored
implement PVWatts (#195)
* implement pvwatts dc and ac models * fix np less than 1.10 test bug, fix doc string * update names, add losses * fix docs * fix arg names * add PVSystem.pvwatts_losses, more doc stuff * update docs, variables
1 parent 155ac77 commit 1c02806

File tree

5 files changed

+315
-0
lines changed

5 files changed

+315
-0
lines changed

docs/sphinx/source/whatsnew/v0.4.0.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Enhancements
1818

1919
* Adds the First Solar spectral correction model. (:issue:`115`)
2020
* Adds the Gueymard 1994 integrated precipitable water model. (:issue:`115`)
21+
* Adds the PVWatts DC, AC, and system losses model. (:issue:`195`)
2122

2223

2324
Bug fixes
@@ -28,6 +29,7 @@ Bug fixes
2829
Documentation
2930
~~~~~~~~~~~~~
3031

32+
* Added new terms to the variables documentation. (:issue:`195`)
3133

3234

3335
Other

pvlib/data/variables_style_rules.csv

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ poa_direct;direct/beam irradiation in plane
1616
poa_diffuse;total diffuse irradiation in plane. sum of ground and sky diffuse.
1717
poa_global;global irradiation in plane. sum of diffuse and beam projection.
1818
poa_sky_diffuse;diffuse irradiation in plane from scattered light in the atmosphere (without ground reflected irradiation)
19+
g_poa_effective;broadband plane of array effective irradiance.
1920
surface_tilt;tilt angle of the surface
2021
surface_azimuth;azimuth angle of the surface
2122
solar_zenith;zenith angle of the sun in degrees
@@ -36,3 +37,10 @@ saturation_current;diode saturation current
3637
resistance_series;series resistance
3738
resistance_shunt;shunt resistance
3839
transposition_factor; the gain ratio of the radiation on inclined plane to global horizontal irradiation: :math:`\frac{poa\_global}{ghi}`
40+
pdc0; nameplate DC rating
41+
pdc, dc; dc power
42+
gamma_pdc; module temperature coefficient. Typically in units of 1/C.
43+
pac, ac; ac powe.
44+
eta_inv; inverter efficiency
45+
eta_inv_ref; reference inverter efficiency
46+
eta_inv_nom; nominal inverter efficiency

pvlib/pvsystem.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,40 @@ def scale_voltage_current_power(self, data):
406406
voltage=self.modules_per_string,
407407
current=self.strings_per_inverter)
408408

409+
def pvwatts_dc(self, g_poa_effective, temp_cell):
410+
"""
411+
Calcuates DC power according to the PVWatts model using
412+
:py:func:`pvwatts_dc`, `self.module_parameters['pdc0']`, and
413+
`self.module_parameters['gamma_pdc']`.
414+
415+
See :py:func:`pvwatts_dc` for details.
416+
"""
417+
return pvwatts_dc(g_poa_effective, temp_cell,
418+
self.module_parameters['pdc0'],
419+
self.module_parameters['gamma_pdc'])
420+
421+
def pvwatts_losses(self, **kwargs):
422+
"""
423+
Calculates DC power losses according the PVwatts model using
424+
:py:func:`pvwatts_losses`. No attributes are used in this
425+
calculation, but all keyword arguments will be passed to the
426+
function.
427+
428+
See :py:func:`pvwatts_losses` for details.
429+
"""
430+
return pvwatts_losses(**kwargs)
431+
432+
def pvwatts_ac(self, pdc):
433+
"""
434+
Calculates AC power according to the PVWatts model using
435+
:py:func:`pvwatts_ac`, `self.module_parameters['pdc0']`, and
436+
`eta_inv_nom=self.inverter_parameters['eta_inv_nom']`.
437+
438+
See :py:func:`pvwatts_ac` for details.
439+
"""
440+
return pvwatts_ac(pdc, self.module_parameters['pdc0'],
441+
eta_inv_nom=self.inverter_parameters['eta_inv_nom'])
442+
409443
def localize(self, location=None, latitude=None, longitude=None,
410444
**kwargs):
411445
"""Creates a LocalizedPVSystem object using this object
@@ -1811,3 +1845,149 @@ def scale_voltage_current_power(data, voltage=1, current=1):
18111845
data['p_mp'] *= voltage * current
18121846

18131847
return data
1848+
1849+
1850+
def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
1851+
r"""
1852+
Implements NREL's PVWatts DC power model [1]_:
1853+
1854+
.. math::
1855+
1856+
P_{dc} = \frac{G_{poa eff}}{1000} P_{dc0} ( 1 + \gamma_{pdc} (T_{cell} - T_{ref}))
1857+
1858+
Parameters
1859+
----------
1860+
g_poa_effective: numeric
1861+
Irradiance transmitted to the PV cells in units of W/m**2. To be
1862+
fully consistent with PVWatts, the user must have already
1863+
applied angle of incidence losses, but not soiling, spectral,
1864+
etc.
1865+
temp_cell: numeric
1866+
Cell temperature in degrees C.
1867+
pdc0: numeric
1868+
Nameplate DC rating.
1869+
gamma_pdc: numeric
1870+
The temperature coefficient in units of 1/C. Typically -0.002 to
1871+
-0.005 per degree C.
1872+
temp_ref: numeric
1873+
Cell reference temperature. PVWatts defines it to be 25 C and
1874+
is included here for flexibility.
1875+
1876+
Returns
1877+
-------
1878+
pdc: numeric
1879+
DC power.
1880+
1881+
References
1882+
----------
1883+
.. [1] A. P. Dobos, "PVWatts Version 5 Manual"
1884+
http://pvwatts.nrel.gov/downloads/pvwattsv5.pdf
1885+
(2014).
1886+
"""
1887+
1888+
pdc = (g_poa_effective * 0.001 * pdc0 *
1889+
(1 + gamma_pdc * (temp_cell - temp_ref)))
1890+
1891+
return pdc
1892+
1893+
1894+
def pvwatts_losses(soiling=2, shading=3, snow=0, mismatch=2, wiring=2,
1895+
connections=0.5, lid=1.5, nameplate_rating=1, age=0,
1896+
availability=3):
1897+
r"""
1898+
Implements NREL's PVWatts system loss model [1]_:
1899+
1900+
.. math::
1901+
1902+
L_{total}(\%) = 100 [ 1 - \Pi_i ( 1 - \frac{L_i}{100} ) ]
1903+
1904+
All parameters must be in units of %. Parameters may be
1905+
array-like, though all array sizes must match.
1906+
1907+
Parameters
1908+
----------
1909+
soiling: numeric
1910+
shading: numeric
1911+
snow: numeric
1912+
mismatch: numeric
1913+
wiring: numeric
1914+
connections: numeric
1915+
lid: numeric
1916+
Light induced degradation
1917+
nameplate_rating: numeric
1918+
age: numeric
1919+
availability: numeric
1920+
1921+
Returns
1922+
-------
1923+
losses: numeric
1924+
System losses in units of %.
1925+
1926+
References
1927+
----------
1928+
.. [1] A. P. Dobos, "PVWatts Version 5 Manual"
1929+
http://pvwatts.nrel.gov/downloads/pvwattsv5.pdf
1930+
(2014).
1931+
"""
1932+
1933+
params = [soiling, shading, snow, mismatch, wiring, connections, lid,
1934+
nameplate_rating, age, availability]
1935+
1936+
# manually looping over params allows for numpy/pandas to handle any
1937+
# array-like broadcasting that might be necessary.
1938+
perf = 1
1939+
for param in params:
1940+
perf *= 1 - param/100
1941+
1942+
losses = (1 - perf) * 100.
1943+
1944+
return losses
1945+
1946+
1947+
def pvwatts_ac(pdc, pdc0, eta_inv_nom=0.96, eta_inv_ref=0.9637):
1948+
r"""
1949+
Implements NREL's PVWatts inverter model [1]_.
1950+
1951+
.. math::
1952+
1953+
\eta = \frac{\eta_{nom}}{\eta_{ref}} (-0.0162\zeta - \frac{0.0059}{\zeta} + 0.9858)
1954+
1955+
.. math::
1956+
1957+
P_{ac} = \min(\eta P_{dc}, P_{ac0})
1958+
1959+
where :math:`\zeta=P_{dc}/P_{dc0}` and :math:`P_{dc0}=P_{ac0}/\eta_{nom}`.
1960+
1961+
Parameters
1962+
----------
1963+
pdc: numeric
1964+
DC power.
1965+
pdc0: numeric
1966+
Nameplate DC rating.
1967+
eta_inv_nom: numeric
1968+
Nominal inverter efficiency.
1969+
eta_inv_ref: numeric
1970+
Reference inverter efficiency. PVWatts defines it to be 0.9637
1971+
and is included here for flexibility.
1972+
1973+
Returns
1974+
-------
1975+
pac: numeric
1976+
AC power.
1977+
1978+
References
1979+
----------
1980+
.. [1] A. P. Dobos, "PVWatts Version 5 Manual,"
1981+
http://pvwatts.nrel.gov/downloads/pvwattsv5.pdf
1982+
(2014).
1983+
"""
1984+
1985+
pac0 = eta_inv_nom * pdc0
1986+
zeta = pdc / pdc0
1987+
1988+
eta = eta_inv_nom / eta_inv_ref * (-0.0162*zeta - 0.0059/zeta + 0.9858)
1989+
1990+
pac = eta * pdc
1991+
pac = np.minimum(pac0, pac)
1992+
1993+
return pac

pvlib/test/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import sys
55
import platform
66
import pandas as pd
7+
import numpy as np
78

89

910
try:
@@ -63,3 +64,19 @@ def incompatible_pandas_0131(test):
6364
out = test
6465

6566
return out
67+
68+
69+
def needs_numpy_1_10(test):
70+
"""
71+
Test won't work on numpy 1.10.
72+
"""
73+
74+
major = int(np.__version__.split('.')[0])
75+
minor = int(np.__version__.split('.')[1])
76+
77+
if major == 1 and minor < 10:
78+
out = unittest.skip('needs numpy 1.10')(test)
79+
else:
80+
out = test
81+
82+
return out

pvlib/test/test_pvsystem.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from nose.tools import assert_equals, assert_almost_equals
1010
from pandas.util.testing import assert_series_equal, assert_frame_equal
11+
from numpy.testing import assert_allclose
1112

1213
from pvlib import tmy
1314
from pvlib import pvsystem
@@ -17,6 +18,8 @@
1718
from pvlib import solarposition
1819
from pvlib.location import Location
1920

21+
from . import needs_numpy_1_10
22+
2023
latitude = 32.2
2124
longitude = -111
2225
tus = Location(latitude, longitude, 'US/Arizona', 700, 'Tucson')
@@ -524,3 +527,108 @@ def test_LocalizedPVSystem___repr__():
524527
assert localized_system.__repr__()==('LocalizedPVSystem with tilt:0 and'+
525528
' azimuth: 180 with Module: blah and Inverter: blarg at Latitude: 32 ' +
526529
'and Longitude: -111')
530+
531+
532+
def test_pvwatts_dc_scalars():
533+
expected = 88.65
534+
out = pvsystem.pvwatts_dc(900, 30, 100, -0.003)
535+
assert_allclose(expected, out)
536+
537+
538+
@needs_numpy_1_10
539+
def test_pvwatts_dc_arrays():
540+
irrad_trans = np.array([np.nan, 900, 900])
541+
temp_cell = np.array([30, np.nan, 30])
542+
irrad_trans, temp_cell = np.meshgrid(irrad_trans, temp_cell)
543+
expected = np.array([[ nan, 88.65, 88.65],
544+
[ nan, nan, nan],
545+
[ nan, 88.65, 88.65]])
546+
out = pvsystem.pvwatts_dc(irrad_trans, temp_cell, 100, -0.003)
547+
assert_allclose(expected, out, equal_nan=True)
548+
549+
550+
def test_pvwatts_dc_series():
551+
irrad_trans = pd.Series([np.nan, 900, 900])
552+
temp_cell = pd.Series([30, np.nan, 30])
553+
expected = pd.Series(np.array([ nan, nan, 88.65]))
554+
out = pvsystem.pvwatts_dc(irrad_trans, temp_cell, 100, -0.003)
555+
assert_series_equal(expected, out)
556+
557+
558+
def test_pvwatts_ac_scalars():
559+
expected = 85.58556604752516
560+
out = pvsystem.pvwatts_ac(90, 100, 0.95)
561+
assert_allclose(expected, out)
562+
563+
564+
@needs_numpy_1_10
565+
def test_pvwatts_ac_arrays():
566+
pdc = np.array([[np.nan], [50], [100]])
567+
pdc0 = 100
568+
expected = np.array([[ nan],
569+
[ 47.60843624],
570+
[ 95. ]])
571+
out = pvsystem.pvwatts_ac(pdc, pdc0, 0.95)
572+
assert_allclose(expected, out, equal_nan=True)
573+
574+
575+
def test_pvwatts_ac_series():
576+
pdc = pd.Series([np.nan, 50, 100])
577+
pdc0 = 100
578+
expected = pd.Series(np.array([ nan, 47.608436, 95. ]))
579+
out = pvsystem.pvwatts_ac(pdc, pdc0, 0.95)
580+
assert_series_equal(expected, out)
581+
582+
583+
def test_pvwatts_losses_default():
584+
expected = 14.075660688264469
585+
out = pvsystem.pvwatts_losses()
586+
assert_allclose(expected, out)
587+
588+
589+
@needs_numpy_1_10
590+
def test_pvwatts_losses_arrays():
591+
expected = np.array([nan, 14.934904])
592+
age = np.array([nan, 1])
593+
out = pvsystem.pvwatts_losses(age=age)
594+
assert_allclose(expected, out)
595+
596+
597+
def test_pvwatts_losses_series():
598+
expected = pd.Series([nan, 14.934904])
599+
age = pd.Series([nan, 1])
600+
out = pvsystem.pvwatts_losses(age=age)
601+
assert_series_equal(expected, out)
602+
603+
604+
def make_pvwatts_system():
605+
module_parameters = {'pdc0': 100, 'gamma_pdc': -0.003}
606+
inverter_parameters = {'eta_inv_nom': 0.95}
607+
system = pvsystem.PVSystem(module_parameters=module_parameters,
608+
inverter_parameters=inverter_parameters)
609+
return system
610+
611+
612+
def test_PVSystem_pvwatts_dc():
613+
system = make_pvwatts_system()
614+
irrad_trans = pd.Series([np.nan, 900, 900])
615+
temp_cell = pd.Series([30, np.nan, 30])
616+
expected = pd.Series(np.array([ nan, nan, 88.65]))
617+
out = system.pvwatts_dc(irrad_trans, temp_cell)
618+
assert_series_equal(expected, out)
619+
620+
621+
def test_PVSystem_pvwatts_losses():
622+
system = make_pvwatts_system()
623+
expected = pd.Series([nan, 14.934904])
624+
age = pd.Series([nan, 1])
625+
out = system.pvwatts_losses(age=age)
626+
assert_series_equal(expected, out)
627+
628+
629+
def test_PVSystem_pvwatts_ac():
630+
system = make_pvwatts_system()
631+
pdc = pd.Series([np.nan, 50, 100])
632+
expected = pd.Series(np.array([ nan, 47.608436, 95. ]))
633+
out = system.pvwatts_ac(pdc)
634+
assert_series_equal(expected, out)

0 commit comments

Comments
 (0)