Skip to content

Commit a934358

Browse files
committed
Merge remote-tracking branch 'upstream/main' into pvwatts-doc
2 parents 5f5f8b1 + 65b6810 commit a934358

File tree

7 files changed

+143
-25
lines changed

7 files changed

+143
-25
lines changed

.github/workflows/top-ranked-issues.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ jobs:
3131
3232
- name: Run update_top_ranking_issues.py
3333
run: |
34-
python ./scripts/update_top_ranking_issues.py
34+
python .github/workflows/update_top_ranking_issues.py
File renamed without changes.

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,18 @@ 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`)
50+
* Fix typo in parameter name ``atmos_refract`` in :py:func:`pvlib.solarposition.spa_python`
51+
and :py:func:`pvlib.spa.solar_position`. (:issue:`2532`, :pull:`2592`)
4952

5053

5154
Testing
@@ -66,4 +69,6 @@ Maintenance
6669

6770
Contributors
6871
~~~~~~~~~~~~
72+
* Will Hobbs (:ghuser:`williamhobbs`)
6973
* Cliff Hansen (:ghuser:`cwhanse`)
74+
* Joseph Radford (:ghuser:`josephradford`)

pvlib/pvsystem.py

Lines changed: 86 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2886,52 +2886,117 @@ 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", NREL, Golden, CO, USA,
29292961
Technical Report NREL/TP-6A20-62641, 2014, :doi:`10.2172/1158421`.
2962+
.. [2] B. Marion, "Comparison of Predictive Models for Photovoltaic
2963+
Module Performance," In Proc. 33rd IEEE Photovoltaic Specialists
2964+
Conference (PVSC), San Diego, CA, USA, 2008, pp. 1-6,
2965+
:doi:`10.1109/PVSC.2008.4922586`.
2966+
Pre-print: https://docs.nrel.gov/docs/fy08osti/42511.pdf
29302967
""" # noqa: E501
29312968

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

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

29373002

pvlib/solarposition.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ def spa_python(time, latitude, longitude,
314314
using time.year and time.month from pandas.DatetimeIndex.
315315
For most simulations the default delta_t is sufficient.
316316
The USNO has historical and forecasted delta_t [3]_.
317-
atmos_refrac : float, optional
317+
atmos_refract : float, optional
318318
The approximate atmospheric refraction (in degrees)
319319
at sunrise and sunset.
320320
how : str, optional, default 'numpy'

pvlib/spa.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,7 @@ def solar_position(unixtime, lat, lon, elev, pressure, temp, delta_t,
10571057
degrees C; used for atmospheric correction
10581058
delta_t : float or array
10591059
Difference between terrestrial time and UT1.
1060-
atmos_refrac : float
1060+
atmos_refract : float
10611061
The approximate atmospheric refraction (in degrees)
10621062
at sunrise and sunset.
10631063
numthreads: int, optional, default 8

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)