Skip to content

Commit 1e3dc5f

Browse files
williamhobbscwhanseRDaxiniwholmgren
authored
add marion's adjustment to pvwatts_dc (#2569)
* add marion's adjustment * linting * Update pvlib/pvsystem.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/pvsystem.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/pvsystem.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/pvsystem.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/pvsystem.py Co-authored-by: Cliff Hansen <[email protected]> * more suggested changes * Update pvlib/pvsystem.py doi sphinx Co-authored-by: RDaxini <[email protected]> * Update pvlib/pvsystem.py Co-authored-by: RDaxini <[email protected]> * Update pvlib/pvsystem.py Co-authored-by: RDaxini <[email protected]> * cleaning up docs * Update pvlib/pvsystem.py Co-authored-by: Will Holmgren <[email protected]> * prevent negative power * fix typo in error eqn * fix the right typo this time... * reorder err eqns * add more detail on `cap_adjustment` * return same object type that was input * fix issue with scalars * another fix * add tests * increase test coverage * more test coverage * fix shallow copy issue * Update pvlib/pvsystem.py Co-authored-by: RDaxini <[email protected]> * Update pvlib/pvsystem.py Co-authored-by: RDaxini <[email protected]> * Update pvlib/pvsystem.py Co-authored-by: RDaxini <[email protected]> * update whatsnew * reorganize to clean it up * single comp path on numpy array * added some comments * unit formatting * call keyword arguments as keyword arguments * quick fix * a few more missed keyword args * simplify with_k_and_cap_adjustmen * Update pvlib/pvsystem.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/pvsystem.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/pvsystem.py Co-authored-by: Cliff Hansen <[email protected]> * doc formatting/linting --------- Co-authored-by: Cliff Hansen <[email protected]> Co-authored-by: RDaxini <[email protected]> Co-authored-by: Will Holmgren <[email protected]>
1 parent b70fb0f commit 1e3dc5f

File tree

3 files changed

+137
-22
lines changed

3 files changed

+137
-22
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,13 @@ Enhancements
3737
:py:func:`~pvlib.singlediode.bishop88_mpp`,
3838
:py:func:`~pvlib.singlediode.bishop88_v_from_i`, and
3939
:py:func:`~pvlib.singlediode.bishop88_i_from_v`. (:issue:`2497`, :pull:`2498`)
40+
* Add Marion 2008 non-linear irradiance adjustment factor to
41+
:py:func:`pvlib.pvsystem.pvwatts_dc`. (:issue:`2566`, :pull:`2569`)
4042
* Accelerate :py:func:`~pvlib.pvsystem.singlediode` when scipy>=1.15 is
4143
installed. (:issue:`2497`, :pull:`2571`)
4244
* Add :py:func:`~pvlib.iotools.get_era5`, a function for accessing
4345
ERA5 reanalysis data. (:pull:`2573`)
4446

45-
4647
Documentation
4748
~~~~~~~~~~~~~
4849
* Provide an overview of single-diode modeling functionality in :ref:`singlediode`. (:pull:`2565`)
@@ -66,4 +67,5 @@ Maintenance
6667

6768
Contributors
6869
~~~~~~~~~~~~
70+
* Will Hobbs (:ghuser:`williamhobbs`)
6971
* Cliff Hansen (:ghuser:`cwhanse`)

pvlib/pvsystem.py

Lines changed: 86 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2886,53 +2886,118 @@ def scale_voltage_current_power(data, voltage=1, current=1):
28862886

28872887
@renamed_kwarg_warning(
28882888
"0.13.0", "g_poa_effective", "effective_irradiance")
2889-
def pvwatts_dc(effective_irradiance, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
2889+
def pvwatts_dc(effective_irradiance, temp_cell, pdc0, gamma_pdc, temp_ref=25.,
2890+
k=None, cap_adjustment=False):
28902891
r"""
2891-
Implements NREL's PVWatts DC power model. The PVWatts DC model [1]_ is:
2892-
2893-
.. math::
2894-
2895-
P_{dc} = \frac{G_{poa eff}}{1000} P_{dc0} ( 1 + \gamma_{pdc} (T_{cell} - T_{ref}))
2896-
2897-
Note that ``pdc0`` is also used as a symbol in
2898-
:py:func:`pvlib.inverter.pvwatts`. ``pdc0`` in this function refers to the DC
2899-
power of the modules at reference conditions. ``pdc0`` in
2900-
:py:func:`pvlib.inverter.pvwatts` refers to the DC power input limit of
2901-
the inverter.
2892+
Implement NREL's PVWatts (Version 5) DC power model.
29022893
29032894
Parameters
29042895
----------
29052896
effective_irradiance: numeric
2906-
Irradiance transmitted to the PV cells. To be
2907-
fully consistent with PVWatts, the user must have already
2908-
applied angle of incidence losses, but not soiling, spectral,
2909-
etc. [W/m^2]
2897+
Irradiance transmitted to the PV cells. To be fully consistent with
2898+
PVWatts, the user must have already applied angle of incidence losses,
2899+
but not soiling, spectral, etc. [Wm⁻²]
29102900
temp_cell: numeric
29112901
Cell temperature [C].
29122902
pdc0: numeric
2913-
Power of the modules at 1000 W/m^2 and cell reference temperature. [W]
2903+
Power of the modules at 1000 Wm⁻² and cell reference temperature. [W]
29142904
gamma_pdc: numeric
2915-
The temperature coefficient of power. Typically -0.002 to
2916-
-0.005 per degree C. [1/C]
2905+
The temperature coefficient of power. Typically -0.002 to -0.005 per
2906+
degree C. [1/°C]
29172907
temp_ref: numeric, default 25.0
2918-
Cell reference temperature. PVWatts defines it to be 25 C and
2919-
is included here for flexibility. [C]
2908+
Cell reference temperature. PVWatts defines it to be 25 °C and is
2909+
included here for flexibility. [°C]
2910+
k: numeric, optional
2911+
Irradiance correction factor, defined in [2]_. Typically positive.
2912+
[unitless]
2913+
cap_adjustment: Boolean, default False
2914+
If True, only apply the optional adjustment at and below 1000 Wm⁻²
29202915
29212916
Returns
29222917
-------
29232918
pdc: numeric
29242919
DC power. [W]
29252920
2921+
Notes
2922+
-----
2923+
The PVWatts Version 5 DC model [1]_ is:
2924+
2925+
.. math::
2926+
2927+
P_{dc} = \frac{G_{poa eff}}{1000} P_{dc0} ( 1 + \gamma_{pdc} (T_{cell} - T_{ref}))
2928+
2929+
This model has also been referred to as the power temperature coefficient
2930+
model.
2931+
2932+
An optional adjustment can be applied to :math:`P_{dc}` as described in
2933+
[2]_. The adjustment accounts for the variation in module efficiency with
2934+
irradiance. The piece-wise adjustment to power is parameterized by `k`,
2935+
where `k` is the reduction in actual power at 200 Wm⁻² relative to power
2936+
calculated at 200 Wm⁻² as 0.2*`pdc0`. For example, a module that is rated
2937+
at 500 W at STC but produces 95 W at 200 Wm⁻² (a 5% relative reduction in
2938+
efficiency) would have a value of `k` = 0.01.
2939+
2940+
.. math::
2941+
2942+
k=\frac{0.2P_{dc0}-P_{200}}{P_{dc0}}
2943+
2944+
For positive `k` values, and `k` is typically positive, this adjustment
2945+
would also increase relative efficiency when irradiance is above 1000 Wm⁻².
2946+
This may not be desired, as modules with nonlinear irradiance response
2947+
often have peak efficiency near 1000 Wm⁻², and it is either flat or
2948+
declining at higher irradiance. An optional parameter, `cap_adjustment`,
2949+
can address this by modifying the adjustment from [2]_ to only apply below
2950+
1000 Wm⁻².
2951+
2952+
Note that ``pdc0`` is also used as a symbol in
2953+
:py:func:`pvlib.inverter.pvwatts`. ``pdc0`` in this function refers to the
2954+
DC power of the modules at reference conditions. ``pdc0`` in
2955+
:py:func:`pvlib.inverter.pvwatts` refers to the DC power input limit of
2956+
the inverter.
2957+
29262958
References
29272959
----------
29282960
.. [1] A. P. Dobos, "PVWatts Version 5 Manual"
29292961
http://pvwatts.nrel.gov/downloads/pvwattsv5.pdf
29302962
(2014).
2963+
.. [2] B. Marion, "Comparison of Predictive Models for
2964+
Photovoltaic Module Performance,"
2965+
:doi:`10.1109/PVSC.2008.4922586`,
2966+
https://docs.nrel.gov/docs/fy08osti/42511.pdf
2967+
(2008).
29312968
""" # noqa: E501
29322969

29332970
pdc = (effective_irradiance * 0.001 * pdc0 *
29342971
(1 + gamma_pdc * (temp_cell - temp_ref)))
29352972

2973+
# apply Marion's correction if k is provided
2974+
if k is not None:
2975+
2976+
# preserve input types
2977+
index = pdc.index if isinstance(pdc, pd.Series) else None
2978+
is_scalar = np.isscalar(pdc)
2979+
2980+
# calculate error adjustments
2981+
err_1 = k * (1 - (1 - effective_irradiance / 200)**4)
2982+
err_2 = k * (1000 - effective_irradiance) / (1000 - 200)
2983+
err = np.where(effective_irradiance <= 200, err_1, err_2)
2984+
2985+
# cap adjustment, if needed
2986+
if cap_adjustment:
2987+
err = np.where(effective_irradiance >= 1000, 0, err)
2988+
2989+
# make error adjustment
2990+
pdc = pdc - pdc0 * err
2991+
2992+
# set negative power to zero
2993+
pdc = np.where(pdc < 0, 0, pdc)
2994+
2995+
# preserve input types
2996+
if index is not None:
2997+
pdc = pd.Series(pdc, index=index)
2998+
elif is_scalar:
2999+
pdc = float(pdc)
3000+
29363001
return pdc
29373002

29383003

tests/test_pvsystem.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,54 @@ def test_pvwatts_dc_series():
21812181
assert_series_equal(expected, out)
21822182

21832183

2184+
def test_pvwatts_dc_scalars_with_k():
2185+
expected = 8.9125
2186+
out = pvsystem.pvwatts_dc(100, 30, 100, -0.003, k=0.01)
2187+
assert_allclose(out, expected)
2188+
2189+
2190+
def test_pvwatts_dc_arrays_with_k():
2191+
irrad_trans = np.array([np.nan, 100, 1200])
2192+
temp_cell = np.array([30, np.nan, 30])
2193+
irrad_trans, temp_cell = np.meshgrid(irrad_trans, temp_cell)
2194+
expected = np.array([[nan, 8.9125, 118.45],
2195+
[nan, nan, nan],
2196+
[nan, 8.9125, 118.45]])
2197+
out = pvsystem.pvwatts_dc(irrad_trans, temp_cell, 100, -0.003, k=0.01)
2198+
assert_allclose(out, expected, equal_nan=True)
2199+
2200+
2201+
def test_pvwatts_dc_series_with_k():
2202+
irrad_trans = pd.Series([np.nan, 100, 100, 1200])
2203+
temp_cell = pd.Series([30, np.nan, 30, 30])
2204+
expected = pd.Series(np.array([ nan, nan, 8.9125, 118.45]))
2205+
out = pvsystem.pvwatts_dc(irrad_trans, temp_cell, 100, -0.003, k=0.01)
2206+
assert_series_equal(expected, out)
2207+
2208+
2209+
def test_pvwatts_dc_with_k_and_cap_adjustment():
2210+
irrad_trans = [100, 1200]
2211+
temp_cell = 25
2212+
out = []
2213+
expected = [0, 120.0]
2214+
for irrad in irrad_trans:
2215+
out.append(pvsystem.pvwatts_dc(irrad, temp_cell, 100, -0.003, k=0.15,
2216+
cap_adjustment=True))
2217+
assert_allclose(out, expected)
2218+
2219+
2220+
def test_pvwatts_dc_arrays_with_k_and_cap_adjustment():
2221+
irrad_trans = np.array([np.nan, 100, 1200])
2222+
temp_cell = np.array([30, np.nan, 30])
2223+
irrad_trans, temp_cell = np.meshgrid(irrad_trans, temp_cell)
2224+
expected = np.array([[nan, 8.9125, 118.2],
2225+
[nan, nan, nan],
2226+
[nan, 8.9125, 118.2]])
2227+
out = pvsystem.pvwatts_dc(irrad_trans, temp_cell, 100, -0.003, k=0.01,
2228+
cap_adjustment=True)
2229+
assert_allclose(out, expected, equal_nan=True)
2230+
2231+
21842232
def test_pvwatts_losses_default():
21852233
expected = 14.075660688264469
21862234
out = pvsystem.pvwatts_losses()

0 commit comments

Comments
 (0)